diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/camera.cpp | 291 | ||||
-rw-r--r-- | src/camera.h | 142 | ||||
-rw-r--r-- | src/client.cpp | 22 | ||||
-rw-r--r-- | src/client.h | 20 | ||||
-rw-r--r-- | src/constants.h | 3 | ||||
-rw-r--r-- | src/content_mapnode.cpp | 9 | ||||
-rw-r--r-- | src/defaultsettings.cpp | 2 | ||||
-rw-r--r-- | src/environment.cpp | 23 | ||||
-rw-r--r-- | src/environment.h | 3 | ||||
-rw-r--r-- | src/game.cpp | 228 | ||||
-rw-r--r-- | src/map.cpp | 36 | ||||
-rw-r--r-- | src/map.h | 6 | ||||
-rw-r--r-- | src/mapnode.h | 3 | ||||
-rw-r--r-- | src/server.cpp | 2 | ||||
-rw-r--r-- | src/tile.cpp | 12 | ||||
-rw-r--r-- | src/utility.cpp | 7 | ||||
-rw-r--r-- | src/utility.h | 4 |
18 files changed, 550 insertions, 264 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c3b0960a..80ee0fc64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -142,6 +142,7 @@ set(minetest_SRCS mapblock_mesh.cpp farmesh.cpp keycode.cpp + camera.cpp clouds.cpp clientobject.cpp guiMainMenu.cpp diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 000000000..7c3dfda3c --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,291 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "camera.h" +#include "debug.h" +#include "main.h" // for g_settings +#include "map.h" +#include "player.h" +#include "utility.h" +#include <cmath> + +const s32 BOBFRAMES = 0x1000000; // must be a power of two + +Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): + m_smgr(smgr), + m_playernode(NULL), + m_cameranode(NULL), + m_draw_control(draw_control), + m_viewing_range_min(5.0), + m_viewing_range_max(5.0), + + m_camera_position(0,0,0), + m_camera_direction(0,0,0), + + m_aspect(1.0), + m_fov_x(1.0), + m_fov_y(1.0), + + m_wanted_frametime(0.0), + m_added_frametime(0), + m_added_frames(0), + m_range_old(0), + m_frametime_old(0), + m_frametime_counter(0), + m_time_per_range(30. / 50), // a sane default of 30ms per 50 nodes of range + + m_view_bobbing_anim(0), + m_view_bobbing_state(0) +{ + //dstream<<__FUNCTION_NAME<<std::endl; + + // note: making the camera node a child of the player node + // would lead to unexpected behaviour, so we don't do that. + m_playernode = smgr->addEmptySceneNode(smgr->getRootSceneNode()); + m_cameranode = smgr->addCameraSceneNode(smgr->getRootSceneNode()); + m_cameranode->bindTargetAndRotation(true); + + updateSettings(); +} + +Camera::~Camera() +{ +} + +void Camera::step(f32 dtime) +{ + if (m_view_bobbing_state != 0) + { + s32 offset = MYMAX(dtime * BOBFRAMES, 1); + if (m_view_bobbing_state == 2) + { + // Animation is getting turned off + s32 subanim = (m_view_bobbing_anim & (BOBFRAMES/2-1)); + if (subanim < BOBFRAMES/4) + offset = -1 * MYMIN(offset, subanim); + else + offset = MYMIN(offset, BOBFRAMES/2 - subanim); + } + m_view_bobbing_anim = (m_view_bobbing_anim + offset) & (BOBFRAMES-1); + } +} + +void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) +{ + if (m_playernode == NULL || m_cameranode == NULL) + return; + + // Set player node transformation + m_playernode->setPosition(player->getPosition()); + //m_playernode->setRotation(v3f(player->getPitch(), -1 * player->getYaw(), 0)); + m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0)); + m_playernode->updateAbsolutePosition(); + + // Compute relative camera position and target + v3f relative_cam_pos = player->getEyePosition() - player->getPosition(); + v3f relative_cam_target = v3f(0,0,1); + relative_cam_target.rotateYZBy(player->getPitch()); + relative_cam_target += relative_cam_pos; + + if ((m_view_bobbing_anim & (BOBFRAMES/2-1)) != 0) + { + f32 bobamount = cos(player->getPitch() * M_PI / 180); + bobamount = 2 * MYMIN(bobamount, 0.5); + + f32 bobangle = m_view_bobbing_anim * 2 * M_PI / BOBFRAMES; + f32 bobangle_s = sin(bobangle); + f32 bobangle_c = cos(bobangle); + f32 bobwidth = 0.02 * bobamount / (bobangle_c * bobangle_c + 1); + f32 bobheight = 1.5 * bobwidth; + + relative_cam_pos.X += bobwidth * bobangle_s; + relative_cam_pos.Y += bobheight * bobangle_s * bobangle_c; + } + + // Compute absolute camera position and target + m_playernode->getAbsoluteTransformation().transformVect(m_camera_position, relative_cam_pos); + m_playernode->getAbsoluteTransformation().transformVect(m_camera_direction, relative_cam_target); + m_camera_direction -= m_camera_position; + + // Set camera node transformation + m_cameranode->setPosition(m_camera_position); + // *100.0 helps in large map coordinates + m_cameranode->setTarget(m_camera_position + 100.0 * m_camera_direction); + + // FOV and and aspect ratio + m_aspect = (f32)screensize.X / (f32) screensize.Y; + m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); + m_cameranode->setAspectRatio(m_aspect); + m_cameranode->setFOV(m_fov_y); + // Just so big a value that everything rendered is visible + // Some more allowance that m_viewing_range_max * BS because of active objects etc. + m_cameranode->setFarValue(m_viewing_range_max * BS * 10); + + // Render distance feedback loop + updateViewingRange(frametime); + + // If the player seems to be walking on solid ground, + // view bobbing is enabled and free_move is off, + // start (or continue) the view bobbing animation. + v3f speed = player->getSpeed(); + //dstream<<"speed: ("<<speed.X<<","<<speed.Y<<","<<speed.Z<<")"<<std::endl; + if ((hypot(speed.X, speed.Z) > BS) && + (fabs(speed.Y) < BS/10) && + (g_settings.getBool("view_bobbing") == true) && + (g_settings.getBool("free_move") == false)) + { + // Start animation + m_view_bobbing_state = 1; + } + else if (m_view_bobbing_state == 1) + { + // Stop animation + m_view_bobbing_state = 2; + } +} + +void Camera::updateViewingRange(f32 frametime_in) +{ + if (m_draw_control.range_all) + return; + + m_added_frametime += frametime_in; + m_added_frames += 1; + + // Actually this counter kind of sucks because frametime is busytime + m_frametime_counter -= frametime_in; + if (m_frametime_counter > 0) + return; + m_frametime_counter = 0.2; + + /*dstream<<__FUNCTION_NAME + <<": Collected "<<m_added_frames<<" frames, total of " + <<m_added_frametime<<"s."<<std::endl; + + dstream<<"m_draw_control.blocks_drawn=" + <<m_draw_control.blocks_drawn + <<", m_draw_control.blocks_would_have_drawn=" + <<m_draw_control.blocks_would_have_drawn + <<std::endl;*/ + + m_draw_control.wanted_min_range = m_viewing_range_min; + m_draw_control.wanted_max_blocks = (1.5*m_draw_control.blocks_would_have_drawn)+1; + if (m_draw_control.wanted_max_blocks < 10) + m_draw_control.wanted_max_blocks = 10; + + f32 block_draw_ratio = 1.0; + if (m_draw_control.blocks_would_have_drawn != 0) + { + block_draw_ratio = (f32)m_draw_control.blocks_drawn + / (f32)m_draw_control.blocks_would_have_drawn; + } + + // Calculate the average frametime in the case that all wanted + // blocks had been drawn + f32 frametime = m_added_frametime / m_added_frames / block_draw_ratio; + + m_added_frametime = 0.0; + m_added_frames = 0; + + f32 wanted_frametime_change = m_wanted_frametime - frametime; + //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl; + + // If needed frametime change is small, just return + if (fabs(wanted_frametime_change) < m_wanted_frametime*0.4) + { + //dstream<<"ignoring small wanted_frametime_change"<<std::endl; + return; + } + + f32 range = m_draw_control.wanted_range; + f32 new_range = range; + + f32 d_range = range - m_range_old; + f32 d_frametime = frametime - m_frametime_old; + if (d_range != 0) + { + m_time_per_range = d_frametime / d_range; + } + + // The minimum allowed calculated frametime-range derivative: + // Practically this sets the maximum speed of changing the range. + // The lower this value, the higher the maximum changing speed. + // A low value here results in wobbly range (0.001) + // A high value here results in slow changing range (0.0025) + // SUGG: This could be dynamically adjusted so that when + // the camera is turning, this is lower + //f32 min_time_per_range = 0.0015; + f32 min_time_per_range = 0.0010; + //f32 min_time_per_range = 0.05 / range; + if(m_time_per_range < min_time_per_range) + { + m_time_per_range = min_time_per_range; + //dstream<<"m_time_per_range="<<m_time_per_range<<" (min)"<<std::endl; + } + else + { + //dstream<<"m_time_per_range="<<m_time_per_range<<std::endl; + } + + f32 wanted_range_change = wanted_frametime_change / m_time_per_range; + // Dampen the change a bit to kill oscillations + //wanted_range_change *= 0.9; + //wanted_range_change *= 0.75; + wanted_range_change *= 0.5; + //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl; + + // If needed range change is very small, just return + if(fabs(wanted_range_change) < 0.001) + { + //dstream<<"ignoring small wanted_range_change"<<std::endl; + return; + } + + new_range += wanted_range_change; + + //f32 new_range_unclamped = new_range; + new_range = MYMAX(new_range, m_viewing_range_min); + new_range = MYMIN(new_range, m_viewing_range_max); + /*dstream<<"new_range="<<new_range_unclamped + <<", clamped to "<<new_range<<std::endl;*/ + + m_draw_control.wanted_range = new_range; + + m_range_old = new_range; + m_frametime_old = frametime; +} + +void Camera::updateSettings() +{ + m_viewing_range_min = g_settings.getS16("viewing_range_nodes_min"); + m_viewing_range_min = MYMAX(5.0, m_viewing_range_min); + + m_viewing_range_max = g_settings.getS16("viewing_range_nodes_max"); + m_viewing_range_max = MYMAX(m_viewing_range_min, m_viewing_range_max); + + f32 fov_degrees = g_settings.getFloat("fov"); + fov_degrees = MYMAX(fov_degrees, 10.0); + fov_degrees = MYMIN(fov_degrees, 170.0); + m_fov_y = fov_degrees * M_PI / 180.0; + + f32 wanted_fps = g_settings.getFloat("wanted_fps"); + wanted_fps = MYMAX(wanted_fps, 1.0); + m_wanted_frametime = 1.0 / wanted_fps; +} + diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 000000000..500168f3d --- /dev/null +++ b/src/camera.h @@ -0,0 +1,142 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CAMERA_HEADER +#define CAMERA_HEADER + +#include "common_irrlicht.h" +#include "utility.h" + +class LocalPlayer; +class MapDrawControl; + +/* + Client camera class, manages the player and camera scene nodes, the viewing distance + and performs view bobbing etc. +*/ +class Camera +{ +public: + Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control); + ~Camera(); + + // Get player scene node. + // This node is positioned at the player's torso (without any view bobbing), + // as given by Player::m_position. Yaw is applied but not pitch. + // Things like wielded tools should be positioned relative to this node. + inline scene::ISceneNode* getPlayerNode() const + { + return m_playernode; + } + + // Get camera scene node. + // It has the eye transformation and view bobbing applied. + inline scene::ICameraSceneNode* getCameraNode() const + { + return m_cameranode; + } + + // Get the camera position (in absolute scene coordinates). + // This has view bobbing applied. + inline v3f getPosition() const + { + return m_camera_position; + } + + // Get the camera direction (in absolute camera coordinates). + // This has view bobbing applied. + inline v3f getDirection() const + { + return m_camera_direction; + } + + // Horizontal field of view + inline f32 getFovX() const + { + return m_fov_x; + } + + // Vertical field of view + inline f32 getFovY() const + { + return m_fov_y; + } + + // Get maximum of getFovX() and getFovY() + inline f32 getFovMax() const + { + return MYMAX(m_fov_x, m_fov_y); + } + + // Step the camera: updates the viewing range and view bobbing. + void step(f32 dtime); + + // Update the camera from the local player's position. + // frametime is used to adjust the viewing range. + void update(LocalPlayer* player, f32 frametime, v2u32 screensize); + + // Render distance feedback loop + void updateViewingRange(f32 frametime_in); + + // Update settings from g_settings + void updateSettings(); + +private: + // Scene manager and nodes + scene::ISceneManager* m_smgr; + scene::ISceneNode* m_playernode; + scene::ICameraSceneNode* m_cameranode; + + // draw control + MapDrawControl& m_draw_control; + + // viewing_range_min_nodes setting + f32 m_viewing_range_min; + // viewing_range_max_nodes setting + f32 m_viewing_range_max; + + // Absolute camera position + v3f m_camera_position; + // Absolute camera direction + v3f m_camera_direction; + + // Field of view and aspect ratio stuff + f32 m_aspect; + f32 m_fov_x; + f32 m_fov_y; + + // Stuff for viewing range calculations + f32 m_wanted_frametime; + f32 m_added_frametime; + s16 m_added_frames; + f32 m_range_old; + f32 m_frametime_old; + f32 m_frametime_counter; + f32 m_time_per_range; + + // View bobbing animation frame (0 <= m_view_bobbing < 0x1000000) + s32 m_view_bobbing_anim; + // If 0, view bobbing is off (e.g. player is standing). + // If 1, view bobbing is on (player is walking). + // If 2, view bobbing is getting switched off. + s32 m_view_bobbing_state; +}; + +#endif + diff --git a/src/client.cpp b/src/client.cpp index d491f9d36..71a826a1f 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -189,8 +189,6 @@ Client::Client( ), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_device(device), - camera_position(0,0,0), - camera_direction(0,0,1), m_server_ser_ver(SER_FMT_VER_INVALID), m_inventory_updated(false), m_time_of_day(0), @@ -1980,11 +1978,14 @@ void Client::addNode(v3s16 p, MapNode n) } } -void Client::updateCamera(v3f pos, v3f dir) +void Client::updateCamera(v3f pos, v3f dir, f32 fov) { - m_env.getClientMap().updateCamera(pos, dir); - camera_position = pos; - camera_direction = dir; + m_env.getClientMap().updateCamera(pos, dir, fov); +} + +void Client::renderPostFx() +{ + m_env.getClientMap().renderPostFx(); } MapNode Client::getNode(v3s16 p) @@ -1998,14 +1999,9 @@ NodeMetadata* Client::getNodeMetadata(v3s16 p) return m_env.getMap().getNodeMetadata(p); } -v3f Client::getPlayerPosition(v3f *eye_position) +LocalPlayer* Client::getLocalPlayer() { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - if (eye_position) - *eye_position = player->getEyePosition(); - return player->getPosition(); + return m_env.getLocalPlayer(); } void Client::setPlayerWield(scene::ISceneNode *wield) diff --git a/src/client.h b/src/client.h index 398a3b849..e7a81a111 100644 --- a/src/client.h +++ b/src/client.h @@ -199,16 +199,16 @@ public: // locks envlock void addNode(v3s16 p, MapNode n); - void updateCamera(v3f pos, v3f dir); + void updateCamera(v3f pos, v3f dir, f32 fov); + + void renderPostFx(); // Returns InvalidPositionException if not found MapNode getNode(v3s16 p); // Wrapper to Map NodeMetadata* getNodeMetadata(v3s16 p); - // Get the player position, and optionally put the - // eye position in *eye_position - v3f getPlayerPosition(v3f *eye_position=NULL); + LocalPlayer* getLocalPlayer(); void setPlayerWield(scene::ISceneNode *wield); void setPlayerControl(PlayerControl &control); @@ -303,15 +303,6 @@ public: { return m_access_denied_reason; } - - /* - This should only be used for calling the special drawing stuff in - ClientEnvironment - */ - ClientEnvironment * getEnv() - { - return &m_env; - } private: @@ -343,9 +334,6 @@ private: IrrlichtDevice *m_device; - v3f camera_position; - v3f camera_direction; - // Server serialization version u8 m_server_ser_ver; diff --git a/src/constants.h b/src/constants.h index 1af5f1f1b..9ba10b51c 100644 --- a/src/constants.h +++ b/src/constants.h @@ -44,9 +44,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PI 3.14159 -// This is the same as in minecraft and everything else -#define FOV_ANGLE (PI/2.5) - // The absolute working limit is (2^15 - viewing_range). // I really don't want to make every algorithm to check if it's // going near the limit or not, so this is lower. diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index e18112ae4..89171755e 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -36,6 +36,11 @@ void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness) void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness); void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness); +/* + A conversion table for backwards compatibility. + Maps <=v19 content types to current ones. + Should never be touched. +*/ content_t trans_table_19[21][2] = { {CONTENT_GRASS, 1}, {CONTENT_TREE, 4}, @@ -380,6 +385,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_WATERSOURCE; f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; + f->post_effect_color = video::SColor(64, 100, 100, 200); if(f->special_material == NULL && g_texturesource) { // Flowing water material @@ -428,6 +434,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_WATERSOURCE; f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; + f->post_effect_color = video::SColor(64, 100, 100, 200); if(f->special_material == NULL && g_texturesource) { // Flowing water material @@ -460,6 +467,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_LAVASOURCE; f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; + f->post_effect_color = video::SColor(192, 255, 64, 0); if(f->special_material == NULL && g_texturesource) { // Flowing lava material @@ -509,6 +517,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_LAVASOURCE; f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; + f->post_effect_color = video::SColor(192, 255, 64, 0); if(f->special_material == NULL && g_texturesource) { // Flowing lava material diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1e17f2f3a..163ed0884 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -59,6 +59,8 @@ void set_default_settings() g_settings.setDefault("random_input", "false"); g_settings.setDefault("client_unload_unused_data_timeout", "600"); g_settings.setDefault("enable_fog", "true"); + g_settings.setDefault("fov", "72"); + g_settings.setDefault("view_bobbing", "true"); g_settings.setDefault("new_style_water", "false"); g_settings.setDefault("new_style_leaves", "true"); g_settings.setDefault("smooth_lighting", "true"); diff --git a/src/environment.cpp b/src/environment.cpp index 8103b7110..80f41f9fd 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1940,29 +1940,6 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return m_client_event_queue.pop_front(); } -void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos) -{ - /*LocalPlayer *player = getLocalPlayer(); - assert(player); - v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/ - v3f pos_f = camera_pos; - v3s16 p_nodes = floatToInt(pos_f, BS); - MapNode n = m_map->getNodeNoEx(p_nodes); - if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE) - { - v2u32 ss = driver->getScreenSize(); - core::rect<s32> rect(0,0, ss.X, ss.Y); - driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect); - } - else if(content_features(n).solidness == 2 && - g_settings.getBool("free_move") == false) - { - v2u32 ss = driver->getScreenSize(); - core::rect<s32> rect(0,0, ss.X, ss.Y); - driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect); - } -} - #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index d9248d2ad..055e2b1f6 100644 --- a/src/environment.h +++ b/src/environment.h @@ -406,9 +406,6 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); - - // Post effects - void drawPostFx(video::IVideoDriver* driver, v3f camera_pos); private: ClientMap *m_map; diff --git a/src/game.cpp b/src/game.cpp index 86f1939c3..af4886137 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "materials.h" #include "config.h" #include "clouds.h" +#include "camera.h" #include "farmesh.h" #include "mapblock.h" @@ -49,8 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define FIELD_OF_VIEW_TEST 0 -MapDrawControl draw_control; - // Chat data struct ChatLine { @@ -144,144 +143,6 @@ struct TextDestSignNode : public TextDest }; /* - Render distance feedback loop -*/ -void updateViewingRange(f32 frametime_in, Client *client) -{ - if(draw_control.range_all == true) - return; - - static f32 added_frametime = 0; - static s16 added_frames = 0; - - added_frametime += frametime_in; - added_frames += 1; - - // Actually this counter kind of sucks because frametime is busytime - static f32 counter = 0; - counter -= frametime_in; - if(counter > 0) - return; - //counter = 0.1; - counter = 0.2; - - /*dstream<<__FUNCTION_NAME - <<": Collected "<<added_frames<<" frames, total of " - <<added_frametime<<"s."<<std::endl;*/ - - /*dstream<<"draw_control.blocks_drawn=" - <<draw_control.blocks_drawn - <<", draw_control.blocks_would_have_drawn=" - <<draw_control.blocks_would_have_drawn - <<std::endl;*/ - - float range_min = g_settings.getS16("viewing_range_nodes_min"); - float range_max = g_settings.getS16("viewing_range_nodes_max"); - - // Limit minimum to keep the feedback loop stable - if(range_min < 5) - range_min = 5; - - draw_control.wanted_min_range = range_min; - //draw_control.wanted_max_blocks = (1.5*draw_control.blocks_drawn)+1; - draw_control.wanted_max_blocks = (1.5*draw_control.blocks_would_have_drawn)+1; - if(draw_control.wanted_max_blocks < 10) - draw_control.wanted_max_blocks = 10; - - float block_draw_ratio = 1.0; - if(draw_control.blocks_would_have_drawn != 0) - { - block_draw_ratio = (float)draw_control.blocks_drawn - / (float)draw_control.blocks_would_have_drawn; - } - - // Calculate the average frametime in the case that all wanted - // blocks had been drawn - f32 frametime = added_frametime / added_frames / block_draw_ratio; - - added_frametime = 0.0; - added_frames = 0; - - float wanted_fps = g_settings.getFloat("wanted_fps"); - float wanted_frametime = 1.0 / wanted_fps; - - f32 wanted_frametime_change = wanted_frametime - frametime; - //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl; - - // If needed frametime change is small, just return - if(fabs(wanted_frametime_change) < wanted_frametime*0.4) - { - //dstream<<"ignoring small wanted_frametime_change"<<std::endl; - return; - } - - float range = draw_control.wanted_range; - float new_range = range; - - static s16 range_old = 0; - static f32 frametime_old = 0; - - float d_range = range - range_old; - f32 d_frametime = frametime - frametime_old; - // A sane default of 30ms per 50 nodes of range - static f32 time_per_range = 30. / 50; - if(d_range != 0) - { - time_per_range = d_frametime / d_range; - } - - // The minimum allowed calculated frametime-range derivative: - // Practically this sets the maximum speed of changing the range. - // The lower this value, the higher the maximum changing speed. - // A low value here results in wobbly range (0.001) - // A high value here results in slow changing range (0.0025) - // SUGG: This could be dynamically adjusted so that when - // the camera is turning, this is lower - //float min_time_per_range = 0.0015; - float min_time_per_range = 0.0010; - //float min_time_per_range = 0.05 / range; - if(time_per_range < min_time_per_range) - { - time_per_range = min_time_per_range; - //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl; - } - else - { - //dstream<<"time_per_range="<<time_per_range<<std::endl; - } - - f32 wanted_range_change = wanted_frametime_change / time_per_range; - // Dampen the change a bit to kill oscillations - //wanted_range_change *= 0.9; - //wanted_range_change *= 0.75; - wanted_range_change *= 0.5; - //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl; - - // If needed range change is very small, just return - if(fabs(wanted_range_change) < 0.001) - { - //dstream<<"ignoring small wanted_range_change"<<std::endl; - return; - } - - new_range += wanted_range_change; - - //float new_range_unclamped = new_range; - if(new_range < range_min) - new_range = range_min; - if(new_range > range_max) - new_range = range_max; - - /*dstream<<"new_range="<<new_range_unclamped - <<", clamped to "<<new_range<<std::endl;*/ - - draw_control.wanted_range = new_range; - - range_old = new_range; - frametime_old = frametime; -} - -/* Hotbar draw routine */ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, @@ -847,6 +708,7 @@ void the_game( draw_load_screen(L"Creating client...", driver, font); std::cout<<DTIME<<"Creating client"<<std::endl; + MapDrawControl draw_control; Client client(device, playername.c_str(), password, draw_control); draw_load_screen(L"Resolving address...", driver, font); @@ -950,28 +812,18 @@ void the_game( /* Create the camera node */ - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode( - 0, // Camera parent - v3f(BS*100, BS*2, BS*100), // Look from - v3f(BS*100+1, BS*2, BS*100), // Look to - -1 // Camera ID - ); - - // Do this so children rotate accordingly (tool) - camera->bindTargetAndRotation(true); - - if(camera == NULL) + Camera camera(smgr, draw_control); + if (camera.getPlayerNode() == NULL) + { + error_message = L"Failed to create the player node"; + return; + } + if (camera.getCameraNode() == NULL) { error_message = L"Failed to create the camera node"; return; } - camera->setFOV(FOV_ANGLE); - - // Just so big a value that everything rendered is visible - camera->setFarValue(100000*BS); - f32 camera_yaw = 0; // "right/left" f32 camera_pitch = 0; // "up/down" @@ -979,7 +831,7 @@ void the_game( Tool */ - v3f tool_wield_position(0.6, -0.6, 1.0); + v3f tool_wield_position(0.06*BS, 1.619*BS, 0.1*BS); v3f tool_wield_rotation(-25, 180, -25); float tool_wield_animation = 0.0; scene::IMeshSceneNode *tool_wield; @@ -1004,7 +856,7 @@ void the_game( mesh->addMeshBuffer(buf); buf->drop(); - tool_wield = smgr->addMeshSceneNode(mesh, camera); + tool_wield = smgr->addMeshSceneNode(mesh, camera.getPlayerNode()); mesh->drop(); } tool_wield->setVisible(false); @@ -1210,12 +1062,6 @@ void the_game( device->run(); /* - Viewing range - */ - - updateViewingRange(busytime, &client); - - /* FPS limiter */ @@ -1606,10 +1452,6 @@ void the_game( } } - // Get player position - v3f camera_position; - v3f player_position = client.getPlayerPosition(&camera_position); - //TimeTaker //timer2("//timer2"); /* @@ -1662,25 +1504,25 @@ void the_game( first_loop_after_window_activation = true; } - camera_yaw = wrapDegrees(camera_yaw); - camera_pitch = wrapDegrees(camera_pitch); - - v3f camera_direction = v3f(0,0,1); - camera_direction.rotateYZBy(camera_pitch); - camera_direction.rotateXZBy(camera_yaw); + LocalPlayer* player = client.getLocalPlayer(); + camera.update(player, busytime, screensize); + camera.step(dtime); - camera->setPosition(camera_position); - // *100.0 helps in large map coordinates - camera->setTarget(camera_position + camera_direction * 100.0); + v3f player_position = player->getPosition(); + v3f camera_position = camera.getPosition(); + v3f camera_direction = camera.getDirection(); + f32 camera_fov = camera.getFovMax(); - if(FIELD_OF_VIEW_TEST){ - client.updateCamera(v3f(0,0,0), v3f(0,0,1)); + if(FIELD_OF_VIEW_TEST) + { + client.updateCamera(v3f(0,0,0), v3f(0,0,1), camera_fov); } - else{ - //TimeTaker timer("client.updateCamera"); - client.updateCamera(camera_position, camera_direction); + else + { + client.updateCamera(camera_position, + camera_direction, camera_fov); } - + //timer2.stop(); //TimeTaker //timer3("//timer3"); @@ -2060,8 +1902,6 @@ void the_game( Calculate stuff for drawing */ - camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y); - u32 daynight_ratio = client.getDayNightRatio(); u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000); video::SColor bgcolor = video::SColor( @@ -2082,7 +1922,7 @@ void the_game( update_skybox(driver, smgr, skybox, brightness); /* - Update coulds + Update clouds */ if(clouds) { @@ -2372,6 +2212,13 @@ void the_game( } /* + Post effects + */ + { + client.renderPostFx(); + } + + /* Frametime log */ if(g_settings.getBool("frametime_graph") == true) @@ -2411,13 +2258,6 @@ void the_game( guienv->drawAll(); /* - Environment post fx - */ - { - client.getEnv()->drawPostFx(driver, camera_position); - } - - /* Draw hotbar */ { diff --git a/src/map.cpp b/src/map.cpp index 7de79c75d..b1908fe2e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3604,7 +3604,8 @@ ClientMap::ClientMap( m_client(client), m_control(control), m_camera_position(0,0,0), - m_camera_direction(0,0,1) + m_camera_direction(0,0,1), + m_camera_fov(M_PI) { m_camera_mutex.Init(); assert(m_camera_mutex.IsInitialized()); @@ -3713,6 +3714,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* @@ -3805,7 +3807,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, - camera_direction, range, &d) == false) + camera_direction, camera_fov, + range, &d) == false) { continue; } @@ -3925,6 +3928,35 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/ } +void ClientMap::renderPostFx() +{ + // Sadly ISceneManager has no "post effects" render pass, in that case we + // could just register for that and handle it in renderMap(). + + m_camera_mutex.Lock(); + v3f camera_position = m_camera_position; + m_camera_mutex.Unlock(); + + MapNode n = getNodeNoEx(floatToInt(camera_position, BS)); + + // - If the player is in a solid node, make everything black. + // - If the player is in liquid, draw a semi-transparent overlay. + ContentFeatures& features = content_features(n); + video::SColor post_effect_color = features.post_effect_color; + if(features.solidness == 2 && g_settings.getBool("free_move") == false) + { + post_effect_color = video::SColor(255, 0, 0, 0); + } + if (post_effect_color.getAlpha() != 0) + { + // Draw a full-screen rectangle + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + v2u32 ss = driver->getScreenSize(); + core::rect<s32> rect(0,0, ss.X, ss.Y); + driver->draw2DRectangle(post_effect_color, rect); + } +} + bool ClientMap::setTempMod(v3s16 p, NodeMod mod, core::map<v3s16, MapBlock*> *affected_blocks) { @@ -518,11 +518,12 @@ public: ISceneNode::drop(); } - void updateCamera(v3f pos, v3f dir) + void updateCamera(v3f pos, v3f dir, f32 fov) { JMutexAutoLock lock(m_camera_mutex); m_camera_position = pos; m_camera_direction = dir; + m_camera_fov = fov; } /* @@ -552,6 +553,8 @@ public: void renderMap(video::IVideoDriver* driver, s32 pass); + void renderPostFx(); + /* Methods for setting temporary modifications to nodes for drawing. @@ -601,6 +604,7 @@ private: v3f m_camera_position; v3f m_camera_direction; + f32 m_camera_fov; JMutex m_camera_mutex; core::map<v2s16, bool> m_last_drawn_sectors; diff --git a/src/mapnode.h b/src/mapnode.h index 3ad67aaf6..19dfb2802 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -160,6 +160,8 @@ struct ContentFeatures u8 liquid_viscosity; // Used currently for flowing liquids u8 vertex_alpha; + // Post effect color, drawn when the camera is inside the node. + video::SColor post_effect_color; // Special irrlicht material, used sometimes video::SMaterial *special_material; AtlasPointer *special_atlas; @@ -197,6 +199,7 @@ struct ContentFeatures liquid_alternative_source = CONTENT_IGNORE; liquid_viscosity = 0; vertex_alpha = 255; + post_effect_color = video::SColor(0, 0, 0, 0); special_material = NULL; special_atlas = NULL; light_source = 0; diff --git a/src/server.cpp b/src/server.cpp index 14d8942cb..fd93d7523 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -584,7 +584,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Don't generate or send if not in sight */ - if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false) + if(isBlockInSight(p, camera_pos, camera_dir, M_PI, 10000*BS) == false) { continue; } diff --git a/src/tile.cpp b/src/tile.cpp index d4244bd42..73f2a85ea 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -504,8 +504,16 @@ void TextureSource::buildMainAtlas() } /* - A list of stuff to add. This should contain as much of the - stuff shown in game as possible, to minimize texture changes. + A list of stuff to include in the texture atlas. + + It is a single-dimensional texture atlas due to the need to tile + textures. + + It should contain as much of the stuff shown in game as possible, + to minimize texture changes. + + It fills up quickly, so do not add anything that isn't contained + in most MapBlocks. E.g. mese isn't suitable but stone is. */ core::array<std::string> sourcelist; diff --git a/src/utility.cpp b/src/utility.cpp index 0721100cb..9c1edc8a9 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -162,8 +162,8 @@ void mysrand(unsigned seed) camera_dir: an unit vector pointing to camera direction range: viewing range */ -bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, - f32 *distance_ptr) +bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, + f32 camera_fov, f32 range, f32 *distance_ptr) { v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; @@ -211,8 +211,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, cosangle += block_max_radius / dforward; // If block is not in the field of view, skip it - //if(cosangle < cos(FOV_ANGLE/2)) - if(cosangle < cos(FOV_ANGLE/2. * 4./3.)) + if(cosangle < cos(camera_fov / 2)) return false; } diff --git a/src/utility.h b/src/utility.h index ea7c11846..d331bafbb 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1765,8 +1765,8 @@ inline int myrand_range(int min, int max) Miscellaneous functions */ -bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, - f32 *distance_ptr=NULL); +bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, + f32 camera_fov, f32 range, f32 *distance_ptr=NULL); /* Queue with unique values with fast checking of value existence |