diff options
Diffstat (limited to 'src/client')
53 files changed, 2010 insertions, 1900 deletions
diff --git a/src/client/camera.cpp b/src/client/camera.cpp index abc55e4b7..350b685e1 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include "noise.h" // easeCurve #include "sound.h" -#include "event.h" +#include "mtevent.h" #include "nodedef.h" #include "util/numeric.h" #include "constants.h" @@ -79,6 +79,7 @@ Camera::Camera(MapDrawControl &draw_control, Client *client): m_cache_fov = std::fmax(g_settings->getFloat("fov"), 45.0f); m_arm_inertia = g_settings->getBool("arm_inertia"); m_nametags.clear(); + m_show_nametag_backgrounds = g_settings->getBool("show_nametag_backgrounds"); } Camera::~Camera() @@ -691,46 +692,47 @@ void Camera::drawNametags() core::matrix4 trans = m_cameranode->getProjectionMatrix(); trans *= m_cameranode->getViewMatrix(); - for (std::list<Nametag *>::const_iterator - i = m_nametags.begin(); - i != m_nametags.end(); ++i) { - Nametag *nametag = *i; - if (nametag->nametag_color.getAlpha() == 0) { - // Enforce hiding nametag, - // because if freetype is enabled, a grey - // shadow can remain. - continue; - } - v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->nametag_pos * BS; + gui::IGUIFont *font = g_fontengine->getFont(); + video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + v2u32 screensize = driver->getScreenSize(); + + for (const Nametag *nametag : m_nametags) { + // Nametags are hidden in GenericCAO::updateNametag() + + v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->pos * BS; f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f }; trans.multiplyWith1x4Matrix(transformed_pos); if (transformed_pos[3] > 0) { std::wstring nametag_colorless = - unescape_translate(utf8_to_wide(nametag->nametag_text)); - core::dimension2d<u32> textsize = - g_fontengine->getFont()->getDimension( + unescape_translate(utf8_to_wide(nametag->text)); + core::dimension2d<u32> textsize = font->getDimension( nametag_colorless.c_str()); f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : core::reciprocal(transformed_pos[3]); - v2u32 screensize = RenderingEngine::get_video_driver()->getScreenSize(); v2s32 screen_pos; screen_pos.X = screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5) - textsize.Width / 2; screen_pos.Y = screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5) - textsize.Height / 2; core::rect<s32> size(0, 0, textsize.Width, textsize.Height); - g_fontengine->getFont()->draw( - translate_string(utf8_to_wide(nametag->nametag_text)).c_str(), - size + screen_pos, nametag->nametag_color); + core::rect<s32> bg_size(-2, 0, textsize.Width+2, textsize.Height); + + auto bgcolor = nametag->getBgColor(m_show_nametag_backgrounds); + if (bgcolor.getAlpha() != 0) + driver->draw2DRectangle(bgcolor, bg_size + screen_pos); + + font->draw( + translate_string(utf8_to_wide(nametag->text)).c_str(), + size + screen_pos, nametag->textcolor); } } } Nametag *Camera::addNametag(scene::ISceneNode *parent_node, - const std::string &nametag_text, video::SColor nametag_color, - const v3f &pos) + const std::string &text, video::SColor textcolor, + Optional<video::SColor> bgcolor, const v3f &pos) { - Nametag *nametag = new Nametag(parent_node, nametag_text, nametag_color, pos); + Nametag *nametag = new Nametag(parent_node, text, textcolor, bgcolor, pos); m_nametags.push_back(nametag); return nametag; } diff --git a/src/client/camera.h b/src/client/camera.h index 3a59637bc..6fd8d9aa7 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -25,27 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <ICameraSceneNode.h> #include <ISceneNode.h> #include <list> +#include "util/Optional.h" class LocalPlayer; struct MapDrawControl; class Client; class WieldMeshSceneNode; -struct Nametag { +struct Nametag +{ + scene::ISceneNode *parent_node; + std::string text; + video::SColor textcolor; + Optional<video::SColor> bgcolor; + v3f pos; + Nametag(scene::ISceneNode *a_parent_node, - const std::string &a_nametag_text, - const video::SColor &a_nametag_color, - const v3f &a_nametag_pos): + const std::string &text, + const video::SColor &textcolor, + const Optional<video::SColor> &bgcolor, + const v3f &pos): parent_node(a_parent_node), - nametag_text(a_nametag_text), - nametag_color(a_nametag_color), - nametag_pos(a_nametag_pos) + text(text), + textcolor(textcolor), + bgcolor(bgcolor), + pos(pos) { } - scene::ISceneNode *parent_node; - std::string nametag_text; - video::SColor nametag_color; - v3f nametag_pos; + + video::SColor getBgColor(bool use_fallback) const + { + if (bgcolor) + return bgcolor.value(); + else if (!use_fallback) + return video::SColor(0, 0, 0, 0); + else if (textcolor.getLuminance() > 186) + // Dark background for light text + return video::SColor(50, 50, 50, 50); + else + // Light background for dark text + return video::SColor(50, 255, 255, 255); + } }; enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT}; @@ -164,13 +184,11 @@ public: } Nametag *addNametag(scene::ISceneNode *parent_node, - const std::string &nametag_text, video::SColor nametag_color, - const v3f &pos); + const std::string &text, video::SColor textcolor, + Optional<video::SColor> bgcolor, const v3f &pos); void removeNametag(Nametag *nametag); - const std::list<Nametag *> &getNametags() { return m_nametags; } - void drawNametags(); inline void addArmInertia(f32 player_yaw); @@ -247,4 +265,5 @@ private: bool m_arm_inertia; std::list<Nametag *> m_nametags; + bool m_show_nametag_backgrounds; }; diff --git a/src/client/client.cpp b/src/client/client.cpp index 65e5b3d8c..ef4a3cdfc 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -129,6 +129,7 @@ Client::Client( if (g_settings->getBool("enable_minimap")) { m_minimap = new Minimap(this); } + m_cache_save_interval = g_settings->getU16("server_map_save_interval"); } @@ -158,20 +159,6 @@ void Client::loadMods() scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); m_script->loadModFromMemory(BUILTIN_MOD_NAME); - // TODO Uncomment when server-sent CSM and verifying of builtin are complete - /* - // Don't load client-provided mods if disabled by server - if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOAD_CLIENT_MODS)) { - warningstream << "Client-provided mod loading is disabled by server." << - std::endl; - // If builtin integrity is wrong, disconnect user - if (!checkBuiltinIntegrity()) { - // TODO disconnect user - } - return; - } - */ - ClientModConfiguration modconf(getClientModsLuaPath()); m_mods = modconf.getMods(); // complain about mods with unsatisfied dependencies @@ -215,12 +202,6 @@ void Client::loadMods() m_script->on_minimap_ready(m_minimap); } -bool Client::checkBuiltinIntegrity() -{ - // TODO - return true; -} - void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path, std::string mod_subpath) { @@ -238,18 +219,13 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo infostream << "Client::scanModSubfolder(): Loading \"" << real_path << "\" as \"" << vfs_path << "\"." << std::endl; - std::ifstream is(real_path, std::ios::binary | std::ios::ate); - if(!is.good()) { + std::string contents; + if (!fs::ReadFile(real_path, contents)) { errorstream << "Client::scanModSubfolder(): Can't read file \"" << real_path << "\"." << std::endl; continue; } - auto size = is.tellg(); - std::string contents(size, '\0'); - is.seekg(0); - is.read(&contents[0], size); - infostream << " size: " << size << " bytes" << std::endl; m_mod_vfs.emplace(vfs_path, contents); } } @@ -330,6 +306,8 @@ Client::~Client() } delete m_minimap; + m_minimap = nullptr; + delete m_media_downloader; } @@ -1218,7 +1196,7 @@ void Client::sendChatMessage(const std::wstring &message) if (canSendChatMessage()) { u32 now = time(NULL); float time_passed = now - m_last_chat_message_sent; - m_last_chat_message_sent = time(NULL); + m_last_chat_message_sent = now; m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f); if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S) @@ -1297,9 +1275,8 @@ void Client::sendPlayerPos() // Save bandwidth by only updating position when // player is not dead and something changed - // FIXME: This part causes breakages in mods like 3d_armor, and has been commented for now - // if (m_activeobjects_received && player->isDead()) - // return; + if (m_activeobjects_received && player->isDead()) + return; if ( player->last_position == player->getPosition() && @@ -1307,7 +1284,7 @@ void Client::sendPlayerPos() player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == player->keyPressed && - player->last_camera_fov == camera_fov && + player->last_camera_fov == camera_fov && player->last_wanted_range == wanted_range) return; @@ -1669,11 +1646,6 @@ ClientEvent *Client::getClientEvent() return event; } -bool Client::connectedToServer() -{ - return m_con->Connected(); -} - const Address Client::getServerAddress() { return m_con->GetPeerAddress(PEER_ID_SERVER); @@ -1860,7 +1832,7 @@ void Client::makeScreenshot() sstr << "Failed to save screenshot '" << filename << "'"; } pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - narrow_to_wide(sstr.str()))); + utf8_to_wide(sstr.str()))); infostream << sstr.str() << std::endl; image->drop(); } diff --git a/src/client/client.h b/src/client/client.h index 733634db1..25a1b97ba 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -223,6 +223,7 @@ public: void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); void handleCommand_MediaPush(NetworkPacket *pkt); + void handleCommand_MinimapModes(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -337,7 +338,6 @@ public: u16 getProtoVersion() { return m_proto_ver; } - bool connectedToServer(); void confirmRegistration(); bool m_is_registration_confirmation_state = false; bool m_simple_singleplayer_mode; @@ -415,11 +415,6 @@ public: return m_csm_restriction_flags & flag; } - u32 getCSMNodeRangeLimit() const - { - return m_csm_restriction_noderange; - } - inline std::unordered_map<u32, u32> &getHUDTranslationMap() { return m_hud_server_to_client; @@ -437,7 +432,6 @@ public: } private: void loadMods(); - bool checkBuiltinIntegrity(); // Virtual methods from con::PeerHandler void peerAdded(con::Peer *peer) override; @@ -587,7 +581,6 @@ private: // Client modding ClientScripting *m_script = nullptr; - bool m_modding_enabled; std::unordered_map<std::string, ModMetadata *> m_mod_storages; float m_mod_storage_save_timer = 10.0f; std::vector<ModSpec> m_mods; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 895b0193c..fc7cbe254 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "scripting_client.h" #include "mapblock_mesh.h" -#include "event.h" +#include "mtevent.h" #include "collision.h" #include "nodedef.h" #include "profiler.h" @@ -51,12 +51,8 @@ public: ~CAOShaderConstantSetter() override = default; - void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) override + void onSetConstants(video::IMaterialRendererServices *services) override { - if (!is_highlevel) - return; - // Ambient color video::SColorf emissive_color(m_emissive_color); @@ -183,84 +179,61 @@ void ClientEnvironment::step(float dtime) if(dtime > 0.5) dtime = 0.5; - f32 dtime_downcount = dtime; - /* Stuff that has a maximum time increment */ - u32 loopcount = 0; - do - { - loopcount++; - - f32 dtime_part; - if(dtime_downcount > dtime_max_increment) - { - dtime_part = dtime_max_increment; - dtime_downcount -= dtime_part; - } - else - { - dtime_part = dtime_downcount; - /* - Setting this to 0 (no -=dtime_part) disables an infinite loop - when dtime_part is so small that dtime_downcount -= dtime_part - does nothing - */ - dtime_downcount = 0; - } - + u32 steps = ceil(dtime / dtime_max_increment); + f32 dtime_part = dtime / steps; + for (; steps > 0; --steps) { /* - Handle local player + Local player handling */ - { - // Control local player - lplayer->applyControl(dtime_part, this); - - // Apply physics - if (!free_move && !is_climbing) { - // Gravity - v3f speed = lplayer->getSpeed(); - if (!lplayer->in_liquid) - speed.Y -= lplayer->movement_gravity * - lplayer->physics_override_gravity * dtime_part * 2.0f; - - // Liquid floating / sinking - if (lplayer->in_liquid && !lplayer->swimming_vertical && - !lplayer->swimming_pitch) - speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; - - // Liquid resistance - if (lplayer->in_liquid_stable || lplayer->in_liquid) { - // How much the node's viscosity blocks movement, ranges - // between 0 and 1. Should match the scale at which viscosity - // increase affects other liquid attributes. - static const f32 viscosity_factor = 0.3f; - - v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; - f32 dl = d_wanted.getLength(); - if (dl > lplayer->movement_liquid_fluidity_smooth) - dl = lplayer->movement_liquid_fluidity_smooth; - - dl *= (lplayer->liquid_viscosity * viscosity_factor) + - (1 - viscosity_factor); - v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); - speed += d; - } - - lplayer->setSpeed(speed); + // Control local player + lplayer->applyControl(dtime_part, this); + + // Apply physics + if (!free_move && !is_climbing) { + // Gravity + v3f speed = lplayer->getSpeed(); + if (!lplayer->in_liquid) + speed.Y -= lplayer->movement_gravity * + lplayer->physics_override_gravity * dtime_part * 2.0f; + + // Liquid floating / sinking + if (lplayer->in_liquid && !lplayer->swimming_vertical && + !lplayer->swimming_pitch) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; + + // Liquid resistance + if (lplayer->in_liquid_stable || lplayer->in_liquid) { + // How much the node's viscosity blocks movement, ranges + // between 0 and 1. Should match the scale at which viscosity + // increase affects other liquid attributes. + static const f32 viscosity_factor = 0.3f; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + if (dl > lplayer->movement_liquid_fluidity_smooth) + dl = lplayer->movement_liquid_fluidity_smooth; + + dl *= (lplayer->liquid_viscosity * viscosity_factor) + + (1 - viscosity_factor); + v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); + speed += d; } - /* - Move the lplayer. - This also does collision detection. - */ - lplayer->move(dtime_part, this, position_max_increment, - &player_collisions); + lplayer->setSpeed(speed); } - } while (dtime_downcount > 0.001); + + /* + Move the lplayer. + This also does collision detection. + */ + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal(); @@ -361,28 +334,6 @@ GenericCAO* ClientEnvironment::getGenericCAO(u16 id) return NULL; } -bool isFreeClientActiveObjectId(const u16 id, - ClientActiveObjectMap &objects) -{ - return id != 0 && objects.find(id) == objects.end(); - -} - -u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects) -{ - // try to reuse id's as late as possible - static u16 last_used_id = 0; - u16 startid = last_used_id; - for(;;) { - last_used_id ++; - if (isFreeClientActiveObjectId(last_used_id, objects)) - return last_used_id; - - if (last_used_id == startid) - return 0; - } -} - u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) { // Register object. If failed return zero id diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index f18915a55..2bb0bc385 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -56,6 +56,20 @@ bool isMenuActive() // Passed to menus to allow disconnecting and exiting MainGameCallback *g_gamecallback = nullptr; +#if 0 +// This can be helpful for the next code cleanup +static void dump_start_data(const GameStartData &data) +{ + std::cout << + "\ndedicated " << (int)data.is_dedicated_server << + "\nport " << data.socket_port << + "\nworld_path " << data.world_spec.path << + "\nworld game " << data.world_spec.gameid << + "\ngame path " << data.game_spec.path << + "\nplayer name " << data.name << + "\naddress " << data.address << std::endl; +} +#endif ClientLauncher::~ClientLauncher() { @@ -74,9 +88,16 @@ ClientLauncher::~ClientLauncher() } -bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) +bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) { - init_args(game_params, cmd_args); + /* This function is called when a client must be started. + * Covered cases: + * - Singleplayer (address but map provided) + * - Join server (no map but address provided) + * - Local server (for main menu only) + */ + + init_args(start_data, cmd_args); // List video modes if requested if (list_video_modes) @@ -154,7 +175,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) } } #endif - g_fontengine = new FontEngine(g_settings, guienv); + g_fontengine = new FontEngine(guienv); FATAL_ERROR_IF(g_fontengine == NULL, "Font engine creation failed."); #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 @@ -216,7 +237,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) core::rect<s32>(0, 0, 10000, 10000)); bool game_has_run = launch_game(error_message, reconnect_requested, - game_params, cmd_args); + start_data, cmd_args); // Reset the reconnect_requested flag reconnect_requested = false; @@ -241,13 +262,6 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) break; } - if (current_playername.length() > PLAYERNAME_SIZE-1) { - error_message = gettext("Player name too long."); - playername = current_playername.substr(0, PLAYERNAME_SIZE-1); - g_settings->set("name", playername); - continue; - } - RenderingEngine::get_video_driver()->setTextureCreationFlag( video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map")); @@ -258,18 +272,11 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) the_game( kill, - random_input, input, - worldspec.path, - current_playername, - current_password, - current_address, - current_port, + start_data, error_message, chat_backend, - &reconnect_requested, - gamespec, - simple_singleplayer_mode + &reconnect_requested ); RenderingEngine::get_scene_manager()->clear(); @@ -311,31 +318,27 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) return retval; } -void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args) +void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_args) { - skip_main_menu = cmd_args.getFlag("go"); - // FIXME: This is confusing (but correct) - - /* If world_path is set then override it unless skipping the main menu using - * the --go command line param. Else, give preference to the address - * supplied on the command line - */ - address = g_settings->get("address"); - if (!game_params.world_path.empty() && !skip_main_menu) - address = ""; - else if (cmd_args.exists("address")) - address = cmd_args.get("address"); + start_data.address = g_settings->get("address"); + if (cmd_args.exists("address")) { + // Join a remote server + start_data.address = cmd_args.get("address"); + start_data.world_path.clear(); + start_data.name = g_settings->get("name"); + } + if (!start_data.world_path.empty()) { + // Start a singleplayer instance + start_data.address = ""; + } - playername = g_settings->get("name"); if (cmd_args.exists("name")) - playername = cmd_args.get("name"); + start_data.name = cmd_args.get("name"); list_video_modes = cmd_args.getFlag("videomodes"); - use_freetype = g_settings->getBool("freetype"); - random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); } @@ -375,28 +378,20 @@ void ClientLauncher::init_input() } bool ClientLauncher::launch_game(std::string &error_message, - bool reconnect_requested, GameParams &game_params, + bool reconnect_requested, GameStartData &start_data, const Settings &cmd_args) { - // Initialize menu data - MainMenuData menudata; - menudata.address = address; - menudata.name = playername; - menudata.password = password; - menudata.port = itos(game_params.socket_port); - menudata.script_data.errormessage = error_message; - menudata.script_data.reconnect_requested = reconnect_requested; - + // Prepare and check the start data to launch a game + std::string error_message_lua = error_message; error_message.clear(); if (cmd_args.exists("password")) - menudata.password = cmd_args.get("password"); - + start_data.password = cmd_args.get("password"); if (cmd_args.exists("password-file")) { std::ifstream passfile(cmd_args.get("password-file")); if (passfile.good()) { - getline(passfile, menudata.password); + getline(passfile, start_data.password); } else { error_message = gettext("Provided password file " "failed to open: ") @@ -407,88 +402,106 @@ bool ClientLauncher::launch_game(std::string &error_message, } // If a world was commanded, append and select it - if (!game_params.world_path.empty()) { - worldspec.gameid = getWorldGameId(game_params.world_path, true); - worldspec.name = _("[--world parameter]"); + // This is provieded by "get_world_from_cmdline()", main.cpp + if (!start_data.world_path.empty()) { + auto &spec = start_data.world_spec; + + spec.path = start_data.world_path; + spec.gameid = getWorldGameId(spec.path, true); + spec.name = _("[--world parameter]"); - if (worldspec.gameid.empty()) { // Create new - worldspec.gameid = g_settings->get("default_game"); - worldspec.name += " [new]"; + if (spec.gameid.empty()) { // Create new + spec.gameid = g_settings->get("default_game"); + spec.name += " [new]"; } - worldspec.path = game_params.world_path; } /* Show the GUI menu */ + std::string server_name, server_description; if (!skip_main_menu) { + // Initialize menu data + // TODO: Re-use existing structs (GameStartData) + MainMenuData menudata; + menudata.address = start_data.address; + menudata.name = start_data.name; + menudata.password = start_data.password; + menudata.port = itos(start_data.socket_port); + menudata.script_data.errormessage = error_message_lua; + menudata.script_data.reconnect_requested = reconnect_requested; + main_menu(&menudata); // Skip further loading if there was an exit signal. if (*porting::signal_handler_killstatus()) return false; - address = menudata.address; + if (!menudata.script_data.errormessage.empty()) { + /* The calling function will pass this back into this function upon the + * next iteration (if any) causing it to be displayed by the GUI + */ + error_message = menudata.script_data.errormessage; + return false; + } + int newport = stoi(menudata.port); if (newport != 0) - game_params.socket_port = newport; - - simple_singleplayer_mode = menudata.simple_singleplayer_mode; + start_data.socket_port = newport; + // Update world information using main menu data std::vector<WorldSpec> worldspecs = getAvailableWorlds(); - if (menudata.selected_world >= 0 - && menudata.selected_world < (int)worldspecs.size()) { + int world_index = menudata.selected_world; + if (world_index >= 0 && world_index < (int)worldspecs.size()) { g_settings->set("selected_world_path", - worldspecs[menudata.selected_world].path); - worldspec = worldspecs[menudata.selected_world]; + worldspecs[world_index].path); + start_data.world_spec = worldspecs[world_index]; } + + start_data.name = menudata.name; + start_data.password = menudata.password; + start_data.address = std::move(menudata.address); + server_name = menudata.servername; + server_description = menudata.serverdescription; + + start_data.local_server = !menudata.simple_singleplayer_mode && + start_data.address.empty(); + } else { + start_data.local_server = !start_data.world_path.empty() && + start_data.address.empty() && !start_data.name.empty(); } - if (!menudata.script_data.errormessage.empty()) { - /* The calling function will pass this back into this function upon the - * next iteration (if any) causing it to be displayed by the GUI - */ - error_message = menudata.script_data.errormessage; + if (!RenderingEngine::run()) return false; - } - if (menudata.name.empty() && !simple_singleplayer_mode) { + if (!start_data.isSinglePlayer() && start_data.name.empty()) { error_message = gettext("Please choose a name!"); errorstream << error_message << std::endl; return false; } - playername = menudata.name; - password = menudata.password; - - current_playername = playername; - current_password = password; - current_address = address; - current_port = game_params.socket_port; - // If using simple singleplayer mode, override - if (simple_singleplayer_mode) { - assert(!skip_main_menu); - current_playername = "singleplayer"; - current_password = ""; - current_address = ""; - current_port = myrand_range(49152, 65535); + if (start_data.isSinglePlayer()) { + start_data.name = "singleplayer"; + start_data.password = ""; + start_data.socket_port = myrand_range(49152, 65535); } else { - g_settings->set("name", playername); - if (!address.empty()) { - ServerListSpec server; - server["name"] = menudata.servername; - server["address"] = menudata.address; - server["port"] = menudata.port; - server["description"] = menudata.serverdescription; - ServerList::insert(server); - } + g_settings->set("name", start_data.name); + } + + if (start_data.name.length() > PLAYERNAME_SIZE - 1) { + error_message = gettext("Player name too long."); + start_data.name.resize(PLAYERNAME_SIZE); + g_settings->set("name", start_data.name); + return false; } + auto &worldspec = start_data.world_spec; infostream << "Selected world: " << worldspec.name << " [" << worldspec.path << "]" << std::endl; - if (current_address.empty()) { // If local game + if (start_data.address.empty()) { + // For singleplayer and local server if (worldspec.path.empty()) { error_message = gettext("No world selected and no address " "provided. Nothing to do."); @@ -504,8 +517,8 @@ bool ClientLauncher::launch_game(std::string &error_message, } // Load gamespec for required game - gamespec = findWorldSubgame(worldspec.path); - if (!gamespec.isValid() && !game_params.game_spec.isValid()) { + start_data.game_spec = findWorldSubgame(worldspec.path); + if (!start_data.game_spec.isValid()) { error_message = gettext("Could not find or load game \"") + worldspec.gameid + "\""; errorstream << error_message << std::endl; @@ -515,15 +528,7 @@ bool ClientLauncher::launch_game(std::string &error_message, if (porting::signal_handler_killstatus()) return true; - if (game_params.game_spec.isValid() && - game_params.game_spec.id != worldspec.gameid) { - warningstream << "Overriding gamespec from \"" - << worldspec.gameid << "\" to \"" - << game_params.game_spec.id << "\"" << std::endl; - gamespec = game_params.game_spec; - } - - if (!gamespec.isValid()) { + if (!start_data.game_spec.isValid()) { error_message = gettext("Invalid gamespec."); error_message += " (world.gameid=" + worldspec.gameid + ")"; errorstream << error_message << std::endl; @@ -531,6 +536,7 @@ bool ClientLauncher::launch_game(std::string &error_message, } } + start_data.world_path = start_data.world_spec.path; return true; } diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 2702895d6..b280d8e6b 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -32,15 +32,15 @@ public: ~ClientLauncher(); - bool run(GameParams &game_params, const Settings &cmd_args); + bool run(GameStartData &start_data, const Settings &cmd_args); -protected: - void init_args(GameParams &game_params, const Settings &cmd_args); +private: + void init_args(GameStartData &start_data, const Settings &cmd_args); bool init_engine(); void init_input(); bool launch_game(std::string &error_message, bool reconnect_requested, - GameParams &game_params, const Settings &cmd_args); + GameStartData &start_data, const Settings &cmd_args); void main_menu(MainMenuData *menudata); @@ -48,23 +48,8 @@ protected: bool list_video_modes = false; bool skip_main_menu = false; - bool use_freetype = false; bool random_input = false; - std::string address = ""; - std::string playername = ""; - std::string password = ""; InputHandler *input = nullptr; MyEventReceiver *receiver = nullptr; gui::IGUISkin *skin = nullptr; - gui::IGUIFont *font = nullptr; - SubgameSpec gamespec; - WorldSpec worldspec; - bool simple_singleplayer_mode = false; - - // These are set up based on the menu and other things - // TODO: Are these required since there's already playername, password, etc - std::string current_playername = "inv£lid"; - std::string current_password = ""; - std::string current_address = "does-not-exist"; - int current_port = 0; }; diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 3e4ab2e94..b9e0cc2ce 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -31,12 +31,43 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <algorithm> #include "client/renderingengine.h" +// struct MeshBufListList +void MeshBufListList::clear() +{ + for (auto &list : lists) + list.clear(); +} + +void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer) +{ + // Append to the correct layer + std::vector<MeshBufList> &list = lists[layer]; + const video::SMaterial &m = buf->getMaterial(); + for (MeshBufList &l : list) { + // comparing a full material is quite expensive so we don't do it if + // not even first texture is equal + if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) + continue; + + if (l.m == m) { + l.bufs.emplace_back(position, buf); + return; + } + } + MeshBufList l; + l.m = m; + l.bufs.emplace_back(position, buf); + list.emplace_back(l); +} + +// ClientMap + ClientMap::ClientMap( Client *client, MapDrawControl &control, s32 id ): - Map(dout_client, client), + Map(client), scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), RenderingEngine::get_scene_manager(), id), m_client(client), @@ -122,20 +153,20 @@ void ClientMap::updateDrawList() } m_drawlist.clear(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + const v3f camera_position = m_camera_position; + const v3f camera_direction = m_camera_direction; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. - // Or maybe they aren't? Well whatever. - camera_fov *= 1.2; + const f32 camera_fov = m_camera_fov * 1.1f; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + // Number of blocks currently loaded by the client + u32 blocks_loaded = 0; // Number of blocks with mesh in rendering range u32 blocks_in_range_with_mesh = 0; // Number of blocks occlusion culled @@ -160,6 +191,7 @@ void ClientMap::updateDrawList() MapSector *sector = sector_it.second; v2s16 sp = sector->getPos(); + blocks_loaded += sector->size(); if (!m_control.range_all) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) @@ -181,8 +213,10 @@ void ClientMap::updateDrawList() if not seen on display */ - if (block->mesh) - block->mesh->updateCameraOffset(m_camera_offset); + if (!block->mesh) { + // Ignore if mesh doesn't exist + continue; + } float range = 100000 * BS; if (!m_control.range_all) @@ -193,13 +227,6 @@ void ClientMap::updateDrawList() camera_direction, camera_fov, range, &d)) continue; - - /* - Ignore if mesh doesn't exist - */ - if (!block->mesh) - continue; - blocks_in_range_with_mesh++; /* @@ -228,52 +255,9 @@ void ClientMap::updateDrawList() g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh); g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled); g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size()); + g_profiler->avg("MapBlocks loaded [#]", blocks_loaded); } -struct MeshBufList -{ - video::SMaterial m; - std::vector<scene::IMeshBuffer*> bufs; -}; - -struct MeshBufListList -{ - /*! - * Stores the mesh buffers of the world. - * The array index is the material's layer. - * The vector part groups vertices by material. - */ - std::vector<MeshBufList> lists[MAX_TILE_LAYERS]; - - void clear() - { - for (auto &list : lists) - list.clear(); - } - - void add(scene::IMeshBuffer *buf, u8 layer) - { - // Append to the correct layer - std::vector<MeshBufList> &list = lists[layer]; - const video::SMaterial &m = buf->getMaterial(); - for (MeshBufList &l : list) { - // comparing a full material is quite expensive so we don't do it if - // not even first texture is equal - if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) - continue; - - if (l.m == m) { - l.bufs.push_back(buf); - return; - } - } - MeshBufList l; - l.m = m; - l.bufs.push_back(buf); - list.push_back(l); - } -}; - void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; @@ -293,19 +277,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) /* Get animation parameters */ - float animation_time = m_client->getAnimationTime(); - int crack = m_client->getCrackLevel(); - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + const float animation_time = m_client->getAnimationTime(); + const int crack = m_client->getCrackLevel(); + const u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + const v3f camera_position = m_camera_position; + const v3f camera_direction = m_camera_direction; + const f32 camera_fov = m_camera_fov; /* Get all blocks and draw all visible ones */ u32 vertex_count = 0; + u32 drawcall_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; @@ -318,6 +303,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) MeshBufListList drawbufs; for (auto &i : m_drawlist) { + v3s16 block_pos = i.first; MapBlock *block = i.second; // If the mesh of the block happened to get deleted, ignore it @@ -383,7 +369,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); - drawbufs.add(buf, layer); + drawbufs.add(buf, block_pos, layer); } } } @@ -392,6 +378,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) TimeTaker draw("Drawing mesh buffers"); + core::matrix4 m; // Model matrix + v3f offset = intToFloat(m_camera_offset, BS); + // Render all layers in order for (auto &lists : drawbufs.lists) { for (MeshBufList &list : lists) { @@ -403,7 +392,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } driver->setMaterial(list.m); - for (scene::IMeshBuffer *buf : list.bufs) { + drawcall_count += list.bufs.size(); + for (auto &pair : list.bufs) { + scene::IMeshBuffer *buf = pair.second; + + v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS); + m.setTranslation(block_wpos - offset); + + driver->setTransform(video::ETS_WORLD, m); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); } @@ -417,6 +413,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } g_profiler->avg(prefix + "vertices drawn [#]", vertex_count); + g_profiler->avg(prefix + "drawcalls [#]", drawcall_count); } static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, @@ -608,5 +605,3 @@ void ClientMap::PrintInfo(std::ostream &out) { out<<"ClientMap: "; } - - diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 172e3a1d6..57cc4427e 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -35,6 +35,25 @@ struct MapDrawControl bool show_wireframe = false; }; +struct MeshBufList +{ + video::SMaterial m; + std::vector<std::pair<v3s16,scene::IMeshBuffer*>> bufs; +}; + +struct MeshBufListList +{ + /*! + * Stores the mesh buffers of the world. + * The array index is the material's layer. + * The vector part groups vertices by material. + */ + std::vector<MeshBufList> lists[MAX_TILE_LAYERS]; + + void clear(); + void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer); +}; + class Client; class ITextureSource; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 8cd3b6bcc..c4c08c05d 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client) fetch_request.request_id = m_httpfetch_next_id; // == i fetch_request.timeout = m_httpfetch_timeout; fetch_request.connect_timeout = m_httpfetch_timeout; - fetch_request.post_data = required_hash_set; + fetch_request.method = HTTP_POST; + fetch_request.raw_data = required_hash_set; fetch_request.extra_headers.emplace_back( "Content-Type: application/octet-stream"); diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 887a62f25..253dee8b9 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -170,8 +170,9 @@ void Clouds::render() // Read noise - bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2]; - + std::vector<char> grid(m_cloud_radius_i * 2 * m_cloud_radius_i * 2); // vector<bool> is broken + std::vector<video::S3DVertex> vertices; + vertices.reserve(16 * m_cloud_radius_i * m_cloud_radius_i); for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) { u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i; @@ -195,12 +196,7 @@ void Clouds::render() { s16 zi = zi0; s16 xi = xi0; - // Draw from front to back (needed for transparency) - /*if(zi <= 0) - zi = -m_cloud_radius_i - zi; - if(xi <= 0) - xi = -m_cloud_radius_i - xi;*/ - // Draw from back to front + // Draw from back to front for proper transparency if(zi >= 0) zi = m_cloud_radius_i - zi - 1; if(xi >= 0) @@ -220,17 +216,10 @@ void Clouds::render() video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 0) }; - /*if(zi <= 0 && xi <= 0){ - v[0].Color.setBlue(255); - v[1].Color.setBlue(255); - v[2].Color.setBlue(255); - v[3].Color.setBlue(255); - }*/ - - f32 rx = cloud_size / 2.0f; + const f32 rx = cloud_size / 2.0f; // if clouds are flat, the top layer should be at the given height - f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; - f32 rz = cloud_size / 2; + const f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; + const f32 rz = cloud_size / 2; for(int i=0; i<num_faces_to_draw; i++) { @@ -320,15 +309,25 @@ void Clouds::render() v3f pos(p0.X, m_params.height * BS, p0.Y); pos -= intToFloat(m_camera_offset, BS); - for (video::S3DVertex &vertex : v) + for (video::S3DVertex &vertex : v) { vertex.Pos += pos; - u16 indices[] = {0,1,2,2,3,0}; - driver->drawVertexPrimitiveList(v, 4, indices, 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); + vertices.push_back(vertex); + } } } - - delete[] grid; + int quad_count = vertices.size() / 4; + std::vector<u16> indices; + indices.reserve(quad_count * 6); + for (int k = 0; k < quad_count; k++) { + indices.push_back(4 * k + 0); + indices.push_back(4 * k + 1); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 3); + indices.push_back(4 * k + 0); + } + driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count, + video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); // Restore fog settings driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density, @@ -342,14 +341,13 @@ void Clouds::step(float dtime) void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse) { + video::SColorf ambient(m_params.color_ambient); + video::SColorf bright(m_params.color_bright); m_camera_pos = camera_p; - m_color.r = MYMIN(MYMAX(color_diffuse.r * m_params.color_bright.getRed(), - m_params.color_ambient.getRed()), 255) / 255.0f; - m_color.g = MYMIN(MYMAX(color_diffuse.g * m_params.color_bright.getGreen(), - m_params.color_ambient.getGreen()), 255) / 255.0f; - m_color.b = MYMIN(MYMAX(color_diffuse.b * m_params.color_bright.getBlue(), - m_params.color_ambient.getBlue()), 255) / 255.0f; - m_color.a = m_params.color_bright.getAlpha() / 255.0f; + m_color.r = core::clamp(color_diffuse.r * bright.r, ambient.r, 1.0f); + m_color.g = core::clamp(color_diffuse.g * bright.g, ambient.g, 1.0f); + m_color.b = core::clamp(color_diffuse.b * bright.b, ambient.b, 1.0f); + m_color.a = bright.a; // is the camera inside the cloud mesh? m_camera_inside_cloud = false; // default diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 4f949f6b0..97ae9afc4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <algorithm> #include <cmath> #include "client/shader.h" +#include "client/minimap.h" class Settings; struct ToolCapabilities; @@ -353,6 +354,8 @@ void GenericCAO::initialize(const std::string &data) m_is_local_player = true; m_is_visible = false; player->setCAO(this); + + m_prop.show_on_minimap = false; } } @@ -371,7 +374,7 @@ void GenericCAO::processInitData(const std::string &data) } // PROTOCOL_VERSION >= 37 - m_name = deSerializeString(is); + m_name = deSerializeString16(is); m_is_player = readU8(is); m_id = readU16(is); m_position = readV3F32(is); @@ -381,7 +384,7 @@ void GenericCAO::processInitData(const std::string &data) const u8 num_messages = readU8(is); for (int i = 0; i < num_messages; i++) { - std::string message = deSerializeLongString(is); + std::string message = deSerializeString32(is); processMessage(message); } @@ -456,18 +459,21 @@ void GenericCAO::setChildrenVisible(bool toset) for (u16 cao_id : m_attachment_child_ids) { GenericCAO *obj = m_env->getGenericCAO(cao_id); if (obj) { - obj->setVisible(toset); + // Check if the entity is forced to appear in first person. + obj->setVisible(obj->m_force_visible ? true : toset); } } } -void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +void GenericCAO::setAttachment(int parent_id, const std::string &bone, + v3f position, v3f rotation, bool force_visible) { int old_parent = m_attachment_parent_id; m_attachment_parent_id = parent_id; m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; + m_force_visible = force_visible; ClientActiveObject *parent = m_env->getActiveObject(parent_id); @@ -477,18 +483,31 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit if (parent) parent->addAttachmentChild(m_id); } - - updateAttachments(); + + // Forcibly show attachments if required by set_attach + if (m_force_visible) { + m_is_visible = true; + } else if (!m_is_local_player) { + // Objects attached to the local player should be hidden in first person + m_is_visible = !m_attached_to_local || + m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST; + m_force_visible = false; + } else { + // Local players need to have this set, + // otherwise first person attachments fail. + m_is_visible = true; + } } void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const + v3f *rotation, bool *force_visible) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; *position = m_attachment_position; *rotation = m_attachment_rotation; + *force_visible = m_force_visible; } void GenericCAO::clearChildAttachments() @@ -498,7 +517,7 @@ void GenericCAO::clearChildAttachments() int child_id = *m_attachment_child_ids.begin(); if (ClientActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(), v3f()); + child->setAttachment(0, "", v3f(), v3f(), false); removeAttachmentChild(child_id); } @@ -507,9 +526,9 @@ void GenericCAO::clearChildAttachments() void GenericCAO::clearParentAttachment() { if (m_attachment_parent_id) - setAttachment(0, "", m_attachment_position, m_attachment_rotation); + setAttachment(0, "", m_attachment_position, m_attachment_rotation, false); else - setAttachment(0, "", v3f(), v3f()); + setAttachment(0, "", v3f(), v3f(), false); } void GenericCAO::addAttachmentChild(int child_id) @@ -567,6 +586,9 @@ void GenericCAO::removeFromScene(bool permanent) m_client->getCamera()->removeNametag(m_nametag); m_nametag = nullptr; } + + if (m_marker && m_client->getMinimap()) + m_client->getMinimap()->removeMarker(&m_marker); } void GenericCAO::addToScene(ITextureSource *tsrc) @@ -795,11 +817,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc) node->setParent(m_matrixnode); updateNametag(); + updateMarker(); updateNodePos(); updateAnimation(); updateBonePosition(); updateAttachments(); setNodeLight(m_last_light); + updateMeshCulling(); } void GenericCAO::updateLight(u32 day_night_ratio) @@ -885,12 +909,32 @@ u16 GenericCAO::getLightPosition(v3s16 *pos) return 3; } +void GenericCAO::updateMarker() +{ + if (!m_client->getMinimap()) + return; + + if (!m_prop.show_on_minimap) { + if (m_marker) + m_client->getMinimap()->removeMarker(&m_marker); + return; + } + + if (m_marker) + return; + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + m_marker = m_client->getMinimap()->addMarker(node); +} + void GenericCAO::updateNametag() { if (m_is_local_player) // No nametag for local player return; - if (m_prop.nametag.empty()) { + if (m_prop.nametag.empty() || m_prop.nametag_color.getAlpha() == 0) { // Delete nametag if (m_nametag) { m_client->getCamera()->removeNametag(m_nametag); @@ -908,12 +952,14 @@ void GenericCAO::updateNametag() if (!m_nametag) { // Add nametag m_nametag = m_client->getCamera()->addNametag(node, - m_prop.nametag, m_prop.nametag_color, pos); + m_prop.nametag, m_prop.nametag_color, + m_prop.nametag_bgcolor, pos); } else { // Update nametag - m_nametag->nametag_text = m_prop.nametag; - m_nametag->nametag_color = m_prop.nametag_color; - m_nametag->nametag_pos = pos; + m_nametag->text = m_prop.nametag; + m_nametag->textcolor = m_prop.nametag_color; + m_nametag->bgcolor = m_prop.nametag_bgcolor; + m_nametag->pos = pos; } } @@ -975,13 +1021,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) if (controls.sneak && walking) new_speed /= 2; - if (walking && (controls.LMB || controls.RMB)) { + if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; player->last_animation = WD_ANIM; - } else if(walking) { + } else if (walking) { new_anim = player->local_animations[1]; player->last_animation = WALK_ANIM; - } else if(controls.LMB || controls.RMB) { + } else if (controls.dig || controls.place) { new_anim = player->local_animations[2]; player->last_animation = DIG_ANIM; } @@ -1004,9 +1050,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Update local player animations if ((player->last_animation != old_anim || - m_animation_speed != old_anim_speed) && - player->last_animation != NO_ANIM && allow_update) - updateAnimation(); + m_animation_speed != old_anim_speed) && + player->last_animation != NO_ANIM && allow_update) + updateAnimation(); } } @@ -1121,7 +1167,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } } - if (!getParent() && std::fabs(m_prop.automatic_rotate) > 0.001) { + if (!getParent() && node && fabs(m_prop.automatic_rotate) > 0.001f) { // This is the child node's rotation. It is only used for automatic_rotate. v3f local_rot = node->getRotation(); local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG * @@ -1130,7 +1176,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } if (!getParent() && m_prop.automatic_face_movement_dir && - (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) { + (fabs(m_velocity.Z) > 0.001f || fabs(m_velocity.X) > 0.001f)) { float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset; float max_rotation_per_sec = @@ -1176,6 +1222,7 @@ void GenericCAO::updateTexturePos() int row = m_tx_basepos.Y; int col = m_tx_basepos.X; + // Yawpitch goes rightwards if (m_tx_select_horiz_by_yawpitch) { if (cam_to_entity.Y > 0.75) col += 5; @@ -1206,6 +1253,27 @@ void GenericCAO::updateTexturePos() float tys = m_tx_size.Y; setBillboardTextureMatrix(m_spritenode, txs, tys, col, row); } + + else if (m_meshnode) { + if (m_prop.visual == "upright_sprite") { + int row = m_tx_basepos.Y; + int col = m_tx_basepos.X; + + // Animation goes downwards + row += m_anim_frame; + + const auto &tx = m_tx_size; + v2f t[4] = { // cf. vertices in GenericCAO::addToScene() + tx * v2f(col+1, row+1), + tx * v2f(col, row+1), + tx * v2f(col, row), + tx * v2f(col+1, row), + }; + auto mesh = m_meshnode->getMesh(); + setMeshBufferTextureCoords(mesh->getMeshBuffer(0), t, 4); + setMeshBufferTextureCoords(mesh->getMeshBuffer(1), t, 4); + } + } } // Do not pass by reference, see header. @@ -1247,7 +1315,7 @@ void GenericCAO::updateTextures(std::string mod) } } - if (m_animated_meshnode) { + else if (m_animated_meshnode) { if (m_prop.visual == "mesh") { for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i) { @@ -1296,8 +1364,8 @@ void GenericCAO::updateTextures(std::string mod) } } } - if(m_meshnode) - { + + else if (m_meshnode) { if(m_prop.visual == "cube") { for (u32 i = 0; i < 6; ++i) @@ -1389,6 +1457,9 @@ void GenericCAO::updateTextures(std::string mod) setMeshColor(mesh, m_prop.colors[0]); } } + // Prevent showing the player after changing texture + if (m_is_local_player) + updateMeshCulling(); } void GenericCAO::updateAnimation() @@ -1558,6 +1629,8 @@ void GenericCAO::processMessage(const std::string &data) u8 cmd = readU8(is); if (cmd == AO_CMD_SET_PROPERTIES) { ObjectProperties newprops; + newprops.show_on_minimap = m_is_player; // default + newprops.deSerialize(is); // Check what exactly changed @@ -1591,6 +1664,8 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + if (m_is_local_player) + m_prop.show_on_minimap = false; if (expire_visuals) { expireVisuals(); @@ -1603,6 +1678,7 @@ void GenericCAO::processMessage(const std::string &data) updateTextures(m_current_texture_modifier); } updateNametag(); + updateMarker(); } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. @@ -1635,7 +1711,7 @@ void GenericCAO::processMessage(const std::string &data) rot_translator.update(m_rotation, false, update_interval); updateNodePos(); } else if (cmd == AO_CMD_SET_TEXTURE_MOD) { - std::string mod = deSerializeString(is); + std::string mod = deSerializeString16(is); // immediately reset a engine issued texture modifier if a mod sends a different one if (m_reset_textures_timer > 0) { @@ -1713,7 +1789,7 @@ void GenericCAO::processMessage(const std::string &data) m_animation_speed = readF32(is); updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d<v3f>(position, rotation); @@ -1721,15 +1797,12 @@ void GenericCAO::processMessage(const std::string &data) // updateBonePosition(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); + bool force_visible = readU8(is); // Returns false for EOF - setAttachment(parent_id, bone, position, rotation); - - // localplayer itself can't be attached to localplayer - if (!m_is_local_player) - m_is_visible = !m_attached_to_local; + setAttachment(parent_id, bone, position, rotation, force_visible); } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); @@ -1771,7 +1844,7 @@ void GenericCAO::processMessage(const std::string &data) int armor_groups_size = readU16(is); for(int i=0; i<armor_groups_size; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); int rating = readS16(is); m_armor_groups[name] = rating; } @@ -1843,5 +1916,43 @@ std::string GenericCAO::debugInfoText() return os.str(); } +void GenericCAO::updateMeshCulling() +{ + if (!m_is_local_player) + return; + + const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST; + + if (m_meshnode && m_prop.visual == "upright_sprite") { + u32 buffers = m_meshnode->getMesh()->getMeshBufferCount(); + for (u32 i = 0; i < buffers; i++) { + video::SMaterial &mat = m_meshnode->getMesh()->getMeshBuffer(i)->getMaterial(); + // upright sprite has no backface culling + mat.setFlag(video::EMF_FRONT_FACE_CULLING, hidden); + } + return; + } + + irr::scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + + if (hidden) { + // Hide the mesh by culling both front and + // back faces. Serious hackyness but it works for our + // purposes. This also preserves the skeletal armature. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + true); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + true); + } else { + // Restore mesh visibility. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + m_prop.backface_culling); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + false); + } +} + // Prototype GenericCAO proto_GenericCAO(NULL, NULL); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 974ff9a1e..7c134fb48 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Camera; class Client; struct Nametag; +struct MinimapMarker; /* SmoothTranslator @@ -84,6 +85,7 @@ private: scene::IBillboardSceneNode *m_spritenode = nullptr; scene::IDummyTransformationSceneNode *m_matrixnode = nullptr; Nametag *m_nametag = nullptr; + MinimapMarker *m_marker = nullptr; v3f m_position = v3f(0.0f, 10.0f * BS, 0); v3f m_velocity; v3f m_acceleration; @@ -109,6 +111,7 @@ private: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attached_to_local = false; + bool m_force_visible = false; int m_anim_frame = 0; int m_anim_num_frames = 1; @@ -216,9 +219,10 @@ public: } void setChildrenVisible(bool toset); - void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void setAttachment(int parent_id, const std::string &bone, v3f position, + v3f rotation, bool force_visible); void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const; + v3f *rotation, bool *force_visible) const; void clearChildAttachments(); void clearParentAttachment(); void addAttachmentChild(int child_id); @@ -248,6 +252,8 @@ public: void updateNametag(); + void updateMarker(); + void updateNodePos(); void step(float dtime, ClientEnvironment *env); @@ -275,4 +281,6 @@ public: { return m_prop.infotext; } + + void updateMeshCulling(); }; diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 3d06584c4..90284ecce 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -513,10 +513,10 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k) count++; } else if (content == CONTENT_AIR) { air_count++; - if (air_count >= 2) - return -0.5 * BS + 0.2; } } + if (air_count >= 2) + return -0.5 * BS + 0.2; if (count > 0) return sum / count; return 0; @@ -723,7 +723,8 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() for (auto &glass_tile : glass_tiles) glass_tile = tiles[4]; - u8 param2 = n.getParam2(); + // Only respect H/V merge bits when paramtype2 = "glasslikeliquidlevel" (liquid tank) + u8 param2 = (f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ? n.getParam2() : 0; bool H_merge = !(param2 & 128); bool V_merge = !(param2 & 64); param2 &= 63; @@ -1454,10 +1455,10 @@ void MapblockMeshGenerator::generate() } } -void MapblockMeshGenerator::renderSingle(content_t node) +void MapblockMeshGenerator::renderSingle(content_t node, u8 param2) { p = {0, 0, 0}; - n = MapNode(node, 0xff, 0x00); + n = MapNode(node, 0xff, param2); f = &nodedef->get(n); drawNode(); } diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 97947cdbe..487d84a07 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -174,5 +174,5 @@ public: public: MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output); void generate(); - void renderSingle(content_t node); + void renderSingle(content_t node, u8 param2 = 0x00); }; diff --git a/src/client/event_manager.h b/src/client/event_manager.h index 3762e89bf..16f7bcf07 100644 --- a/src/client/event_manager.h +++ b/src/client/event_manager.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "event.h" +#include "mtevent.h" #include <list> #include <map> diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 61d52cc2f..47218c0d9 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -42,8 +42,7 @@ static void font_setting_changed(const std::string &name, void *userdata) } /******************************************************************************/ -FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) : - m_settings(main_settings), +FontEngine::FontEngine(gui::IGUIEnvironment* env) : m_env(env) { @@ -51,34 +50,34 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) : i = (FontMode) FONT_SIZE_UNSPECIFIED; } - assert(m_settings != NULL); // pre-condition + assert(g_settings != NULL); // pre-condition assert(m_env != NULL); // pre-condition assert(m_env->getSkin() != NULL); // pre-condition readSettings(); if (m_currentMode == FM_Standard) { - m_settings->registerChangedCallback("font_size", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_bold", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_italic", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_path", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL); - m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_size", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_bold", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_italic", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_path", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL); + g_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL); } else if (m_currentMode == FM_Fallback) { - m_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL); - m_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL); - m_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL); - m_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL); + g_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL); + g_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL); + g_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL); + g_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL); } - m_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL); - m_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL); - m_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL); - m_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL); + g_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL); + g_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL); + g_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL); + g_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL); } /******************************************************************************/ @@ -186,13 +185,28 @@ unsigned int FontEngine::getDefaultFontSize() return m_default_size[m_currentMode]; } +unsigned int FontEngine::getFontSize(FontMode mode) +{ + if (m_currentMode == FM_Simple) { + if (mode == FM_Mono || mode == FM_SimpleMono) + return m_default_size[FM_SimpleMono]; + else + return m_default_size[FM_Simple]; + } + + if (mode == FM_Unspecified) + return m_default_size[FM_Standard]; + + return m_default_size[mode]; +} + /******************************************************************************/ void FontEngine::readSettings() { if (USE_FREETYPE && g_settings->getBool("freetype")) { - m_default_size[FM_Standard] = m_settings->getU16("font_size"); - m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size"); - m_default_size[FM_Mono] = m_settings->getU16("mono_font_size"); + m_default_size[FM_Standard] = g_settings->getU16("font_size"); + m_default_size[FM_Fallback] = g_settings->getU16("fallback_font_size"); + m_default_size[FM_Mono] = g_settings->getU16("mono_font_size"); /*~ DO NOT TRANSLATE THIS LITERALLY! This is a special string. Put either "no" or "yes" @@ -205,15 +219,15 @@ void FontEngine::readSettings() m_currentMode = is_yes(gettext("needs_fallback_font")) ? FM_Fallback : FM_Standard; - m_default_bold = m_settings->getBool("font_bold"); - m_default_italic = m_settings->getBool("font_italic"); + m_default_bold = g_settings->getBool("font_bold"); + m_default_italic = g_settings->getBool("font_italic"); } else { m_currentMode = FM_Simple; } - m_default_size[FM_Simple] = m_settings->getU16("font_size"); - m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size"); + m_default_size[FM_Simple] = g_settings->getU16("font_size"); + m_default_size[FM_SimpleMono] = g_settings->getU16("mono_font_size"); cleanCache(); updateFontCache(); @@ -229,7 +243,7 @@ void FontEngine::updateSkin() m_env->getSkin()->setFont(font); else errorstream << "FontEngine: Default font file: " << - "\n\t\"" << m_settings->get("font_path") << "\"" << + "\n\t\"" << g_settings->get("font_path") << "\"" << "\n\trequired for current screen configuration was not found" << " or was invalid file format." << "\n\tUsing irrlicht default font." << std::endl; @@ -277,7 +291,7 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec) setting_suffix.append("_italic"); u32 size = std::floor(RenderingEngine::getDisplayDensity() * - m_settings->getFloat("gui_scaling") * spec.size); + g_settings->getFloat("gui_scaling") * spec.size); if (size == 0) { errorstream << "FontEngine: attempt to use font size 0" << std::endl; @@ -296,8 +310,8 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec) std::string fallback_settings[] = { wanted_font_path, - m_settings->get("fallback_font_path"), - m_settings->getDefault(setting_prefix + "font_path") + g_settings->get("fallback_font_path"), + Settings::getLayer(SL_DEFAULTS)->get(setting_prefix + "font_path") }; #if USE_FREETYPE @@ -331,7 +345,7 @@ gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec) assert(spec.mode == FM_Simple || spec.mode == FM_SimpleMono); assert(spec.size != FONT_SIZE_UNSPECIFIED); - const std::string &font_path = m_settings->get( + const std::string &font_path = g_settings->get( (spec.mode == FM_SimpleMono) ? "mono_font_path" : "font_path"); size_t pos_dot = font_path.find_last_of('.'); @@ -349,7 +363,7 @@ gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec) u32 size = std::floor( RenderingEngine::getDisplayDensity() * - m_settings->getFloat("gui_scaling") * + g_settings->getFloat("gui_scaling") * spec.size); irr::gui::IGUIFont *font = nullptr; diff --git a/src/client/fontengine.h b/src/client/fontengine.h index 53f14c45f..e27ef60e9 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <map> #include <vector> #include "util/basic_macros.h" +#include "irrlichttypes.h" #include <IGUIFont.h> #include <IGUISkin.h> #include <IGUIEnvironment.h> @@ -48,7 +49,7 @@ struct FontSpec { u16 getHash() { - return (mode << 2) | (bold << 1) | italic; + return (mode << 2) | (static_cast<u8>(bold) << 1) | static_cast<u8>(italic); } unsigned int size; @@ -61,7 +62,7 @@ class FontEngine { public: - FontEngine(Settings* main_settings, gui::IGUIEnvironment* env); + FontEngine(gui::IGUIEnvironment* env); ~FontEngine(); @@ -124,8 +125,8 @@ public: /** get default font size */ unsigned int getDefaultFontSize(); - /** initialize font engine */ - void initialize(Settings* main_settings, gui::IGUIEnvironment* env); + /** get font size for a specific mode */ + unsigned int getFontSize(FontMode mode); /** update internal parameters from settings */ void readSettings(); @@ -146,9 +147,6 @@ private: /** clean cache */ void cleanCache(); - /** pointer to settings for registering callbacks or reading config */ - Settings* m_settings = nullptr; - /** pointer to irrlicht gui environment */ gui::IGUIEnvironment* m_env = nullptr; diff --git a/src/client/game.cpp b/src/client/game.cpp index 069c482ca..3c58fb46f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -34,11 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clouds.h" #include "config.h" #include "content_cao.h" +#include "content/subgames.h" #include "client/event_manager.h" #include "fontengine.h" #include "itemdef.h" #include "log.h" #include "filesys.h" +#include "gameparams.h" #include "gettext.h" #include "gui/guiChatConsole.h" #include "gui/guiConfirmRegistration.h" @@ -169,13 +171,7 @@ struct LocalFormspecHandler : public TextDest return; } - if (fields.find("quit") != fields.end()) { - return; - } - - if (fields.find("btn_continue") != fields.end()) { - return; - } + return; } if (m_formname == "MT_DEATH_SCREEN") { @@ -184,7 +180,7 @@ struct LocalFormspecHandler : public TextDest return; } - if (m_client && m_client->modsLoaded()) + if (m_client->modsLoaded()) m_client->getScript()->on_formspec_input(m_formname, fields); } @@ -422,14 +418,13 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedVertexShaderSetting<float> m_animation_timer_vertex; CachedPixelShaderSetting<float> m_animation_timer_pixel; CachedPixelShaderSetting<float, 3> m_day_light; + CachedPixelShaderSetting<float, 4> m_star_color; CachedPixelShaderSetting<float, 3> m_eye_position_pixel; CachedVertexShaderSetting<float, 3> m_eye_position_vertex; CachedPixelShaderSetting<float, 3> m_minimap_yaw; CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags; Client *m_client; public: @@ -456,14 +451,13 @@ public: m_animation_timer_vertex("animationTimer"), m_animation_timer_pixel("animationTimer"), m_day_light("dayLight"), + m_star_color("starColor"), m_eye_position_pixel("eyePosition"), m_eye_position_vertex("eyePosition"), m_minimap_yaw("yawVec"), m_camera_offset_pixel("cameraOffset"), m_camera_offset_vertex("cameraOffset"), m_base_texture("baseTexture"), - m_normal_texture("normalTexture"), - m_texture_flags("textureFlags"), m_client(client) { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); @@ -475,12 +469,8 @@ public: g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this); } - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) + void onSetConstants(video::IMaterialRendererServices *services) override { - if (!is_highlevel) - return; - // Background color video::SColor bgcolor = m_sky->getBgColor(); video::SColorf bgcolorf(bgcolor); @@ -509,6 +499,10 @@ public: sunlight.b }; m_day_light.set(dnc, services); + video::SColorf star_color = m_sky->getCurrentStarColor(); + float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a}; + m_star_color.set(clr, services); + u32 animation_timer = porting::getTimeMs() % 1000000; float animation_timer_f = (float)animation_timer / 100000.f; m_animation_timer_vertex.set(&animation_timer_f, services); @@ -551,12 +545,8 @@ public: m_camera_offset_pixel.set(camera_offset_array, services); m_camera_offset_vertex.set(camera_offset_array, services); - SamplerLayer_t base_tex = 0, - normal_tex = 1, - flags_tex = 2; + SamplerLayer_t base_tex = 0; m_base_texture.set(&base_tex, services); - m_normal_texture.set(&normal_tex, services); - m_texture_flags.set(&flags_tex, services); } }; @@ -587,7 +577,7 @@ public: virtual IShaderConstantSetter* create() { - GameGlobalShaderConstantSetter *scs = new GameGlobalShaderConstantSetter( + auto *scs = new GameGlobalShaderConstantSetter( m_sky, m_force_fog_off, m_fog_range, m_client); if (!m_sky) created_nosky.push_back(scs); @@ -602,7 +592,6 @@ public: #endif /**************************************************************************** - ****************************************************************************/ const float object_hit_delay = 0.2; @@ -623,15 +612,15 @@ struct GameRunData { u16 new_playeritem; PointedThing pointed_old; bool digging; - bool ldown_for_dig; + bool punching; + bool btn_down_for_dig; bool dig_instantly; bool digging_blocked; - bool left_punch; bool reset_jump_timer; float nodig_delay_timer; float dig_time; float dig_time_complete; - float repeat_rightclick_timer; + float repeat_place_timer; float object_hit_delay_timer; float time_from_last_punch; ClientActiveObject *selected_object; @@ -669,19 +658,11 @@ public: ~Game(); bool startup(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - // If address is "", local server is used and address is updated - std::string *address, - u16 port, + const GameStartData &game_params, std::string &error_message, bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode); + ChatBackend *chat_backend); void run(); void shutdown(); @@ -691,21 +672,18 @@ protected: void extendedResourceCleanup(); // Basic initialisation - bool init(const std::string &map_dir, std::string *address, - u16 port, - const SubgameSpec &gamespec); + bool init(const std::string &map_dir, const std::string &address, + u16 port, const SubgameSpec &gamespec); bool initSound(); bool createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address); + const SubgameSpec &gamespec, u16 port); // Client creation - bool createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port); + bool createClient(const GameStartData &start_data); bool initGui(); // Client connection - bool connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, + bool connectToServer(const GameStartData &start_data, bool *connect_ok, bool *aborted); bool getServerContent(bool *aborted); @@ -796,6 +774,14 @@ protected: { return input->wasKeyDown(k); } + inline bool wasKeyPressed(GameKeyType k) + { + return input->wasKeyPressed(k); + } + inline bool wasKeyReleased(GameKeyType k) + { + return input->wasKeyReleased(k); + } #ifdef __ANDROID__ void handleAndroidChatInput(); @@ -885,7 +871,6 @@ private: bool *reconnect_requested; scene::ISceneNode *skybox; - bool random_input; bool simple_singleplayer_mode; /* End 'cache' */ @@ -910,7 +895,7 @@ private: bool m_cache_enable_free_move; f32 m_cache_mouse_sensitivity; f32 m_cache_joystick_frustum_sensitivity; - f32 m_repeat_right_click_time; + f32 m_repeat_place_time; f32 m_cache_cam_smoothing; f32 m_cache_fog_start; @@ -920,6 +905,7 @@ private: bool m_does_lost_focus_pause_game = false; + int m_reset_HW_buffer_counter = 0; #ifdef __ANDROID__ bool m_cache_hold_aux1; bool m_android_chat_open; @@ -944,7 +930,7 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("joystick_frustum_sensitivity", &settingChangedCallback, this); - g_settings->registerChangedCallback("repeat_rightclick_time", + g_settings->registerChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->registerChangedCallback("noclip", &settingChangedCallback, this); @@ -1002,7 +988,7 @@ Game::~Game() &settingChangedCallback, this); g_settings->deregisterChangedCallback("mouse_sensitivity", &settingChangedCallback, this); - g_settings->deregisterChangedCallback("repeat_rightclick_time", + g_settings->deregisterChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->deregisterChangedCallback("noclip", &settingChangedCallback, this); @@ -1017,28 +1003,21 @@ Game::~Game() } bool Game::startup(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - std::string *address, // can change if simple_singleplayer_mode - u16 port, + const GameStartData &start_data, std::string &error_message, bool *reconnect, - ChatBackend *chat_backend, - const SubgameSpec &gamespec, - bool simple_singleplayer_mode) + ChatBackend *chat_backend) { + // "cache" this->device = RenderingEngine::get_raw_device(); this->kill = kill; this->error_message = &error_message; this->reconnect_requested = reconnect; - this->random_input = random_input; this->input = input; this->chat_backend = chat_backend; - this->simple_singleplayer_mode = simple_singleplayer_mode; + this->simple_singleplayer_mode = start_data.isSinglePlayer(); input->keycache.populate(); @@ -1059,10 +1038,12 @@ bool Game::startup(bool *kill, g_client_translations->clear(); - if (!init(map_dir, address, port, gamespec)) + // address can change if simple_singleplayer_mode + if (!init(start_data.world_spec.path, start_data.address, + start_data.socket_port, start_data.game_spec)) return false; - if (!createClient(playername, password, address, port)) + if (!createClient(start_data)) return false; RenderingEngine::initialize(client, hud); @@ -1221,7 +1202,7 @@ void Game::shutdown() bool Game::init( const std::string &map_dir, - std::string *address, + const std::string &address, u16 port, const SubgameSpec &gamespec) { @@ -1245,8 +1226,8 @@ bool Game::init( return false; // Create a server if not connecting to an existing one - if (address->empty()) { - if (!createSingleplayerServer(map_dir, gamespec, port, address)) + if (address.empty()) { + if (!createSingleplayerServer(map_dir, gamespec, port)) return false; } @@ -1281,7 +1262,7 @@ bool Game::initSound() } bool Game::createSingleplayerServer(const std::string &map_dir, - const SubgameSpec &gamespec, u16 port, std::string *address) + const SubgameSpec &gamespec, u16 port) { showOverlayMessage(N_("Creating server..."), 0, 5); @@ -1308,14 +1289,14 @@ bool Game::createSingleplayerServer(const std::string &map_dir, return false; } - server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); + server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, + false, nullptr, error_message); server->start(); return true; } -bool Game::createClient(const std::string &playername, - const std::string &password, std::string *address, u16 port) +bool Game::createClient(const GameStartData &start_data) { showOverlayMessage(N_("Creating client..."), 0, 10); @@ -1330,8 +1311,7 @@ bool Game::createClient(const std::string &playername, g_touchscreengui->hide(); } #endif - if (!connectToServer(playername, password, address, port, - &could_connect, &connect_aborted)) + if (!connectToServer(start_data, &could_connect, &connect_aborted)) return false; if (!could_connect) { @@ -1352,7 +1332,7 @@ bool Game::createClient(const std::string &playername, return false; } - GameGlobalShaderConstantSetterFactory *scsf = new GameGlobalShaderConstantSetterFactory( + auto *scsf = new GameGlobalShaderConstantSetterFactory( &m_flags.force_fog_off, &runData.fog_range, client); shader_src->addShaderConstantSetterFactory(scsf); @@ -1362,33 +1342,21 @@ bool Game::createClient(const std::string &playername, /* Camera */ camera = new Camera(*draw_control, client); - if (!camera || !camera->successfullyCreated(*error_message)) + if (!camera->successfullyCreated(*error_message)) return false; client->setCamera(camera); /* Clouds */ - if (m_cache_enable_clouds) { + if (m_cache_enable_clouds) clouds = new Clouds(smgr, -1, time(0)); - if (!clouds) { - *error_message = "Memory allocation error (clouds)"; - errorstream << *error_message << std::endl; - return false; - } - } /* Skybox */ - sky = new Sky(-1, texture_src); + sky = new Sky(-1, texture_src, shader_src); scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop - if (!sky) { - *error_message = "Memory allocation error sky"; - errorstream << *error_message << std::endl; - return false; - } - /* Pre-calculated values */ video::ITexture *t = texture_src->getTexture("crack_anylength.png"); @@ -1418,18 +1386,10 @@ bool Game::createClient(const std::string &playername, hud = new Hud(guienv, client, player, &player->inventory); - if (!hud) { - *error_message = "Memory error: could not create HUD"; - errorstream << *error_message << std::endl; - return false; - } - mapper = client->getMinimap(); - if (mapper) { - mapper->setMinimapMode(MINIMAP_MODE_OFF); - if (client->modsLoaded()) - client->getScript()->on_minimap_ready(mapper); - } + + if (mapper && client->modsLoaded()) + client->getScript()->on_minimap_ready(mapper); return true; } @@ -1447,11 +1407,6 @@ bool Game::initGui() // Chat backend and console gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); - if (!gui_chat_console) { - *error_message = "Could not allocate memory for chat console"; - errorstream << *error_message << std::endl; - return false; - } #ifdef HAVE_TOUCHSCREENGUI @@ -1463,8 +1418,7 @@ bool Game::initGui() return true; } -bool Game::connectToServer(const std::string &playername, - const std::string &password, std::string *address, u16 port, +bool Game::connectToServer(const GameStartData &start_data, bool *connect_ok, bool *connection_aborted) { *connect_ok = false; // Let's not be overly optimistic @@ -1473,10 +1427,10 @@ bool Game::connectToServer(const std::string &playername, showOverlayMessage(N_("Resolving address..."), 0, 15); - Address connect_address(0, 0, 0, 0, port); + Address connect_address(0, 0, 0, 0, start_data.socket_port); try { - connect_address.Resolve(address->c_str()); + connect_address.Resolve(start_data.address.c_str()); if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY //connect_address.Resolve("localhost"); @@ -1503,14 +1457,12 @@ bool Game::connectToServer(const std::string &playername, return false; } - client = new Client(playername.c_str(), password, *address, + client = new Client(start_data.name.c_str(), + start_data.password, start_data.address, *draw_control, texture_src, shader_src, itemdef_manager, nodedef_manager, sound, eventmgr, connect_address.isIPv6(), m_game_ui.get()); - if (!client) - return false; - client->m_simple_singleplayer_mode = simple_singleplayer_mode; infostream << "Connecting to server at "; @@ -1574,13 +1526,13 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, + &g_menumgr, client, start_data.name, start_data.password, connection_aborted, texture_src))->drop(); } } else { wait_time += dtime; // Only time out if we aren't waiting for the server we started - if (!address->empty() && wait_time > 10) { + if (!start_data.isSinglePlayer() && wait_time > 10) { *error_message = "Connection timed out."; errorstream << *error_message << std::endl; break; @@ -1659,7 +1611,10 @@ bool Game::getServerContent(bool *aborted) std::stringstream message; std::fixed(message); message.precision(0); - message << gettext("Media...") << " " << (client->mediaReceiveProgress()*100) << "%"; + float receive = client->mediaReceiveProgress() * 100; + message << gettext("Media..."); + if (receive > 0) + message << " " << receive << "%"; message.precision(2); if ((USE_CURL == 0) || @@ -2029,15 +1984,11 @@ void Game::processItemSelection(u16 *new_playeritem) s32 dir = wheel; - if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN) || - wasKeyDown(KeyType::HOTBAR_NEXT)) { + if (wasKeyDown(KeyType::HOTBAR_NEXT)) dir = -1; - } - if (input->joystick.wasKeyDown(KeyType::SCROLL_UP) || - wasKeyDown(KeyType::HOTBAR_PREV)) { + if (wasKeyDown(KeyType::HOTBAR_PREV)) dir = 1; - } if (dir < 0) *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0; @@ -2090,7 +2041,7 @@ void Game::openInventory() TextDest *txt_dst = new TextDestPlayerInventory(client); auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(fs_src->getForm(), inventoryloc); } @@ -2231,52 +2182,37 @@ void Game::toggleMinimap(bool shift_pressed) if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap")) return; - if (shift_pressed) { + if (shift_pressed) mapper->toggleMinimapShape(); - return; - } + else + mapper->nextMode(); + + // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here + // Not so satisying code to keep compatibility with old fixed mode system + // --> u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags; - MinimapMode mode = MINIMAP_MODE_OFF; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { - mode = mapper->getMinimapMode(); - mode = (MinimapMode)((int)mode + 1); - // If radar is disabled and in, or switching to, radar mode - if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE) && mode > 3) - mode = MINIMAP_MODE_OFF; - } + if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) { + m_game_ui->m_flags.show_minimap = false; + } else { - m_game_ui->m_flags.show_minimap = true; - switch (mode) { - case MINIMAP_MODE_SURFACEx1: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x1"); - break; - case MINIMAP_MODE_SURFACEx2: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x2"); - break; - case MINIMAP_MODE_SURFACEx4: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x4"); - break; - case MINIMAP_MODE_RADARx1: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x1"); - break; - case MINIMAP_MODE_RADARx2: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x2"); - break; - case MINIMAP_MODE_RADARx4: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x4"); - break; - default: - mode = MINIMAP_MODE_OFF; - m_game_ui->m_flags.show_minimap = false; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) - m_game_ui->showTranslatedStatusText("Minimap hidden"); - else - m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); - } + // If radar is disabled, try to find a non radar mode or fall back to 0 + if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE)) + while (mapper->getModeIndex() && + mapper->getModeDef().type == MINIMAP_TYPE_RADAR) + mapper->nextMode(); - mapper->setMinimapMode(mode); + m_game_ui->m_flags.show_minimap = mapper->getModeDef().type != + MINIMAP_TYPE_OFF; + } + // <-- + // End of 'not so satifying code' + if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) || + (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP))) + m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label)); + else + m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); } void Game::toggleFog() @@ -2399,10 +2335,10 @@ void Game::checkZoomEnabled() void Game::updateCameraDirection(CameraOrientation *cam, float dtime) { if ((device->isWindowActive() && device->isWindowFocused() - && !isMenuActive()) || random_input) { + && !isMenuActive()) || input->isRandom()) { #ifndef __ANDROID__ - if (!random_input) { + if (!input->isRandom()) { // Mac OSX gets upset if this is set every frame if (device->getCursorControl()->isVisible()) device->getCursorControl()->setVisible(false); @@ -2482,8 +2418,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::SPECIAL1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), - input->getLeftState(), - input->getRightState(), + isKeyDown(KeyType::DIG), + isKeyDown(KeyType::PLACE), cam.camera_pitch, cam.camera_yaw, input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), @@ -2498,8 +2434,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | + ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); @@ -2637,7 +2573,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); } delete event->show_formspec.formspec; @@ -2650,7 +2586,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -2959,7 +2895,8 @@ void Game::updateCamera(u32 busy_time, f32 dtime) camera->toggleCameraMode(); - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + // Make the player visible depending on camera mode. + playercao->updateMeshCulling(); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); } @@ -3081,7 +3018,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) PointedThing pointed = updatePointedThing(shootline, selected_def.liquids_pointable, - !runData.ldown_for_dig, + !runData.btn_down_for_dig, camera_offset); if (pointed != runData.pointed_old) { @@ -3089,20 +3026,18 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) hud->updateSelectionMesh(camera_offset); } - if (runData.digging_blocked && !input->getLeftState()) { - // allow digging again if button is not pressed + // Allow digging again if button is not pressed + if (runData.digging_blocked && !isKeyDown(KeyType::DIG)) runData.digging_blocked = false; - } /* Stop digging when - - releasing left mouse button + - releasing dig button - pointing away from node */ if (runData.digging) { - if (input->getLeftReleased()) { - infostream << "Left button released" - << " (stopped digging)" << std::endl; + if (wasKeyReleased(KeyType::DIG)) { + infostream << "Dig button released (stopped digging)" << std::endl; runData.digging = false; } else if (pointed != runData.pointed_old) { if (pointed.type == POINTEDTHING_NODE @@ -3112,8 +3047,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) // Still pointing to the same node, but a different face. // Don't reset. } else { - infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; + infostream << "Pointing away from node (stopped digging)" << std::endl; runData.digging = false; hud->updateSelectionMesh(camera_offset); } @@ -3124,55 +3058,60 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) client->setCrack(-1, v3s16(0, 0, 0)); runData.dig_time = 0.0; } - } else if (runData.dig_instantly && input->getLeftReleased()) { - // Remove e.g. torches faster when clicking instead of holding LMB + } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) { + // Remove e.g. torches faster when clicking instead of holding dig button runData.nodig_delay_timer = 0; runData.dig_instantly = false; } - if (!runData.digging && runData.ldown_for_dig && !input->getLeftState()) { - runData.ldown_for_dig = false; - } + if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG)) + runData.btn_down_for_dig = false; - runData.left_punch = false; + runData.punching = false; soundmaker->m_player_leftpunch_sound.name = ""; // Prepare for repeating, unless we're not supposed to - if (input->getRightState() && !g_settings->getBool("safe_dig_and_place")) - runData.repeat_rightclick_timer += dtime; + if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place")) + runData.repeat_place_timer += dtime; else - runData.repeat_rightclick_timer = 0; + runData.repeat_place_timer = 0; - if (selected_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->modsLoaded() - || !client->getScript()->on_item_use(selected_item, pointed))) + if (selected_def.usable && isKeyDown(KeyType::DIG)) { + if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() || + !client->getScript()->on_item_use(selected_item, pointed))) client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { handlePointingAtNode(pointed, selected_item, hand_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { v3f player_position = player->getPosition(); handlePointingAtObject(pointed, tool_item, player_position, show_debug); - } else if (input->getLeftState()) { + } else if (isKeyDown(KeyType::DIG)) { // When button is held down in air, show continuous animation - runData.left_punch = true; + runData.punching = true; // Run callback even though item is not usable - if (input->getLeftClicked() && client->modsLoaded()) + if (wasKeyPressed(KeyType::DIG) && client->modsLoaded()) client->getScript()->on_item_use(selected_item, pointed); - } else if (input->getRightClicked()) { + } else if (wasKeyPressed(KeyType::PLACE)) { handlePointingAtNothing(selected_item); } runData.pointed_old = pointed; - if (runData.left_punch || input->getLeftClicked()) - camera->setDigging(0); // left click animation + if (runData.punching || wasKeyPressed(KeyType::DIG)) + camera->setDigging(0); // dig animation - input->resetLeftClicked(); - input->resetRightClicked(); + input->clearWasKeyPressed(); + input->clearWasKeyReleased(); + // Ensure DIG & PLACE are marked as handled + wasKeyDown(KeyType::DIG); + wasKeyDown(KeyType::PLACE); - input->resetLeftReleased(); - input->resetRightReleased(); + input->joystick.clearWasKeyPressed(KeyType::DIG); + input->joystick.clearWasKeyPressed(KeyType::PLACE); + + input->joystick.clearWasKeyReleased(KeyType::DIG); + input->joystick.clearWasKeyReleased(KeyType::PLACE); } @@ -3193,11 +3132,14 @@ PointedThing Game::updatePointedThing( const NodeDefManager *nodedef = map.getNodeDefManager(); runData.selected_object = NULL; + hud->pointing_at_object = false; RaycastState s(shootline, look_for_object, liquids_pointable); PointedThing result; env.continueRaycast(&s, &result); if (result.type == POINTEDTHING_OBJECT) { + hud->pointing_at_object = true; + runData.selected_object = client->getEnv().getActiveObject(result.object_id); aabb3f selection_box; if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() && @@ -3269,7 +3211,7 @@ PointedThing Game::updatePointedThing( void Game::handlePointingAtNothing(const ItemStack &playerItem) { - infostream << "Right Clicked in Air" << std::endl; + infostream << "Attempted to place item while pointing at nothing" << std::endl; PointedThing fauxPointed; fauxPointed.type = POINTEDTHING_NOTHING; client->interact(INTERACT_ACTIVATE, fauxPointed); @@ -3288,7 +3230,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, ClientMap &map = client->getEnv().getClientMap(); - if (runData.nodig_delay_timer <= 0.0 && input->getLeftState() + if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG) && !runData.digging_blocked && client->checkPrivilege("interact")) { handleDigging(pointed, nodepos, selected_item, hand_item, dtime); @@ -3309,13 +3251,14 @@ void Game::handlePointingAtNode(const PointedThing &pointed, } } - if ((input->getRightClicked() || - runData.repeat_rightclick_timer >= m_repeat_right_click_time) && + if ((wasKeyPressed(KeyType::PLACE) || + runData.repeat_place_timer >= m_repeat_place_time) && client->checkPrivilege("interact")) { - runData.repeat_rightclick_timer = 0; - infostream << "Ground right-clicked" << std::endl; + runData.repeat_place_timer = 0; + infostream << "Place button pressed while looking at ground" << std::endl; - camera->setDigging(1); // right click animation (always shown for feedback) + // Placing animation (always shown for feedback) + camera->setDigging(1); soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); @@ -3349,7 +3292,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } // formspec in meta - if (meta && !meta->getString("formspec").empty() && !random_input + if (meta && !meta->getString("formspec").empty() && !input->isRandom() && !isKeyDown(KeyType::SNEAK)) { // on_rightclick callbacks are called anyway if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) @@ -3366,7 +3309,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; @@ -3381,8 +3324,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } verbosestream << "Node placement prediction for " - << selected_def.name << " is " - << prediction << std::endl; + << selected_def.name << " is " << prediction << std::endl; v3s16 p = neighbourpos; // Place inside node itself if buildable_to @@ -3393,6 +3335,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } else { node = map.getNode(p, &is_valid_position); if (is_valid_position && !nodedef->get(node).buildable_to) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -3465,6 +3408,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, pp = p + v3s16(0, -1, 0); if (!nodedef->get(map.getNode(pp)).walkable) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -3541,7 +3485,7 @@ void Game::handlePointingAtObject(const PointedThing &pointed, m_game_ui->setInfoText(infotext); - if (input->getLeftState()) { + if (isKeyDown(KeyType::DIG)) { bool do_punch = false; bool do_punch_damage = false; @@ -3551,12 +3495,12 @@ void Game::handlePointingAtObject(const PointedThing &pointed, runData.object_hit_delay_timer = object_hit_delay; } - if (input->getLeftClicked()) + if (wasKeyPressed(KeyType::DIG)) do_punch = true; if (do_punch) { - infostream << "Left-clicked object" << std::endl; - runData.left_punch = true; + infostream << "Punched object" << std::endl; + runData.punching = true; } if (do_punch_damage) { @@ -3571,8 +3515,8 @@ void Game::handlePointingAtObject(const PointedThing &pointed, if (!disable_send) client->interact(INTERACT_START_DIGGING, pointed); } - } else if (input->getRightClicked()) { - infostream << "Right-clicked object" << std::endl; + } else if (wasKeyDown(KeyType::PLACE)) { + infostream << "Pressed place button while pointing at object" << std::endl; client->interact(INTERACT_PLACE, pointed); // place } } @@ -3618,7 +3562,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, return; client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; - runData.ldown_for_dig = true; + runData.btn_down_for_dig = true; } if (!runData.dig_instantly) { @@ -3712,10 +3656,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, client->setCrack(-1, nodepos); } - camera->setDigging(0); // left click animation + camera->setDigging(0); // Dig animation } - void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam) { @@ -3958,7 +3901,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Update minimap pos and rotation */ - if (mapper && m_game_ui->m_flags.show_minimap && m_game_ui->m_flags.show_hud) { + if (mapper && m_game_ui->m_flags.show_hud) { mapper->setPos(floatToInt(player->getPosition(), BS)); mapper->setAngle(player->getYaw()); } @@ -3966,6 +3909,27 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* End scene */ + if (++m_reset_HW_buffer_counter > 500) { + /* + Periodically remove all mesh HW buffers. + + Work around for a quirk in Irrlicht where a HW buffer is only + released after 20000 iterations (triggered from endScene()). + + Without this, all loaded but unused meshes will retain their HW + buffers for at least 5 minutes, at which point looking up the HW buffers + becomes a bottleneck and the framerate drops (as much as 30%). + + Tests showed that numbers between 50 and 1000 are good, so picked 500. + There are no other public Irrlicht APIs that allow interacting with the + HW buffers without tracking the status of every individual mesh. + + The HW buffers for _visible_ meshes will be reinitialized in the next frame. + */ + infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl; + driver->removeAllHardwareBuffers(); + m_reset_HW_buffer_counter = 0; + } driver->endScene(); stats->drawtime = tt_draw.stop(true); @@ -4001,9 +3965,10 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) else fps_timings->busy_time = 0; - u32 frametime_min = 1000 / (g_menumgr.pausesGame() - ? g_settings->getFloat("pause_fps_max") - : g_settings->getFloat("fps_max")); + u32 frametime_min = 1000 / ( + device->isWindowFocused() && !g_menumgr.pausesGame() + ? g_settings->getFloat("fps_max") + : g_settings->getFloat("fps_max_unfocused")); if (fps_timings->busy_time < frametime_min) { fps_timings->sleep_time = frametime_min - fps_timings->busy_time; @@ -4051,7 +4016,7 @@ void Game::readSettings() m_cache_enable_fog = g_settings->getBool("enable_fog"); m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); - m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); + m_repeat_place_time = g_settings->getFloat("repeat_place_time"); m_cache_enable_noclip = g_settings->getBool("noclip"); m_cache_enable_free_move = g_settings->getBool("free_move"); @@ -4116,7 +4081,7 @@ void Game::showDeathFormspec() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_respawn"); } @@ -4143,30 +4108,32 @@ void Game::showPauseMenu() "- %s: move backwards\n" "- %s: move left\n" "- %s: move right\n" - "- %s: jump/climb\n" - "- %s: sneak/go down\n" + "- %s: jump/climb up\n" + "- %s: dig/punch\n" + "- %s: place/use\n" + "- %s: sneak/climb down\n" "- %s: drop item\n" "- %s: inventory\n" "- Mouse: turn/look\n" - "- Mouse left: dig/punch\n" - "- Mouse right: place/use\n" "- Mouse wheel: select item\n" "- %s: chat\n" ); - char control_text_buf[600]; - - porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), - GET_KEY_NAME(keymap_forward), - GET_KEY_NAME(keymap_backward), - GET_KEY_NAME(keymap_left), - GET_KEY_NAME(keymap_right), - GET_KEY_NAME(keymap_jump), - GET_KEY_NAME(keymap_sneak), - GET_KEY_NAME(keymap_drop), - GET_KEY_NAME(keymap_inventory), - GET_KEY_NAME(keymap_chat) - ); + char control_text_buf[600]; + + porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), + GET_KEY_NAME(keymap_forward), + GET_KEY_NAME(keymap_backward), + GET_KEY_NAME(keymap_left), + GET_KEY_NAME(keymap_right), + GET_KEY_NAME(keymap_jump), + GET_KEY_NAME(keymap_dig), + GET_KEY_NAME(keymap_place), + GET_KEY_NAME(keymap_sneak), + GET_KEY_NAME(keymap_drop), + GET_KEY_NAME(keymap_inventory), + GET_KEY_NAME(keymap_chat) + ); std::string control_text = std::string(control_text_buf); str_formspec_escape(control_text); @@ -4248,7 +4215,7 @@ void Game::showPauseMenu() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_continue"); formspec->doPause = true; } @@ -4260,19 +4227,11 @@ void Game::showPauseMenu() /****************************************************************************/ void the_game(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - const std::string &address, // If empty local server is created - u16 port, - + const GameStartData &start_data, std::string &error_message, ChatBackend &chat_backend, - bool *reconnect_requested, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode) + bool *reconnect_requested) // Used for local game { Game game; @@ -4280,14 +4239,11 @@ void the_game(bool *kill, * is created then this is updated and we don't want to change the value * passed to us by the calling function */ - std::string server_address = address; try { - if (game.startup(kill, random_input, input, map_dir, - playername, password, &server_address, port, error_message, - reconnect_requested, &chat_backend, gamespec, - simple_singleplayer_mode)) { + if (game.startup(kill, input, start_data, error_message, + reconnect_requested, &chat_backend)) { game.run(); } diff --git a/src/client/game.h b/src/client/game.h index 69e6eed0b..d04153271 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class InputHandler; class ChatBackend; /* to avoid having to include chat.h */ struct SubgameSpec; +struct GameStartData; struct Jitter { f32 max, min, avg, counter, max_sample, min_sample, max_fraction; @@ -41,16 +42,10 @@ struct CameraOrientation { f32 camera_pitch; // "up/down" }; + void the_game(bool *kill, - bool random_input, InputHandler *input, - const std::string &map_dir, - const std::string &playername, - const std::string &password, - const std::string &address, // If "", local server is used - u16 port, + const GameStartData &start_data, std::string &error_message, ChatBackend &chat_backend, - bool *reconnect_requested, - const SubgameSpec &gamespec, // Used for local game - bool simple_singleplayer_mode); + bool *reconnect_requested); diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index c216f405d..0c08efeb5 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -61,17 +61,6 @@ void GameUI::init() m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false, false, guiroot); - // At the middle of the screen - // Object infos are shown in this - m_guitext_info = gui::StaticText::add(guienv, L"", - core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) - + v2s32(100, 200), false, true, guiroot); - - // Status text (displays info when showing and hiding GUI stuff, etc.) - m_guitext_status = gui::StaticText::add(guienv, L"<Status>", - core::rect<s32>(0, 0, 0, 0), false, false, guiroot); - m_guitext_status->setVisible(false); - // Chat text m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), //false, false); // Disable word wrap as of now @@ -82,6 +71,20 @@ void GameUI::init() chat_font_size, FM_Unspecified)); } + // At the middle of the screen + // Object infos are shown in this + u32 chat_font_height = m_guitext_chat->getActiveFont()->getDimension(L"Ay").Height; + m_guitext_info = gui::StaticText::add(guienv, L"", + core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + + v2s32(100, chat_font_height * + (g_settings->getU16("recent_chat_messages") + 3)), + false, true, guiroot); + + // Status text (displays info when showing and hiding GUI stuff, etc.) + m_guitext_status = gui::StaticText::add(guienv, L"<Status>", + core::rect<s32>(0, 0, 0, 0), false, false, guiroot); + m_guitext_status->setVisible(false); + // Profiler text (size is updated when text is updated) m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>", core::rect<s32>(0, 0, 0, 0), false, false, guiroot); @@ -113,8 +116,8 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ << std::setprecision(1) << " | view range: " << (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) - << std::setprecision(3) - << " | RTT: " << client->getRTT() << "s"; + << std::setprecision(2) + << " | RTT: " << (client->getRTT() * 1000.0f) << "ms"; setStaticText(m_guitext, utf8_to_wide(os.str()).c_str()); m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X, @@ -133,9 +136,9 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ << "pos: (" << (player_position.X / BS) << ", " << (player_position.Y / BS) << ", " << (player_position.Z / BS) - << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "\xC2\xB0 " + << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° " << yawToDirectionString(cam.camera_yaw) - << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "\xC2\xB0" + << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°" << " | seed: " << ((u64)client->getMapSeed()); if (pointed_old.type == POINTEDTHING_NODE) { diff --git a/src/client/gameui.h b/src/client/gameui.h index 67c6a9921..b6c8a224d 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "irrlichttypes.h" #include <IGUIEnvironment.h> #include "gui/guiFormSpecMenu.h" #include "util/enriched_string.h" diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index 4262331bd..406c096e6 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -172,11 +172,8 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, const core::rect<s32> &rect, const core::rect<s32> &middle, - const core::rect<s32> *cliprect) + const core::rect<s32> *cliprect, const video::SColor *const colors) { - const video::SColor color(255,255,255,255); - const video::SColor colors[] = {color,color,color,color}; - auto originalSize = texture->getOriginalSize(); core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner; diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index b703d91f0..379a4bdb0 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -54,4 +54,5 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, */ void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, const core::rect<s32> &rect, const core::rect<s32> &middle, - const core::rect<s32> *cliprect = nullptr); + const core::rect<s32> *cliprect = nullptr, + const video::SColor *const colors = nullptr); diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 31e633bc2..46736b325 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -36,11 +36,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/renderingengine.h" +#include "client/minimap.h" #ifdef HAVE_TOUCHSCREENGUI #include "gui/touchscreengui.h" #endif +#define OBJECT_CROSSHAIR_LINE_SIZE 8 +#define CROSSHAIR_LINE_SIZE 10 + Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, Inventory *inventory) { @@ -76,6 +80,7 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b); use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png"); + use_object_crosshair_image = tsrc->isKnownSourceImage("object_crosshair.png"); m_selection_boxes.clear(); m_halo_boxes.clear(); @@ -95,7 +100,7 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, if (g_settings->getBool("enable_shaders")) { IShaderSource *shdrsrc = client->getShaderSource(); u16 shader_id = shdrsrc->getShader( - m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1); + m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", TILE_MATERIAL_ALPHA); m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; } else { m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -110,6 +115,28 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, } else { m_selection_material.MaterialType = video::EMT_SOLID; } + + // Prepare mesh for compass drawing + m_rotation_mesh_buffer.Vertices.set_used(4); + m_rotation_mesh_buffer.Indices.set_used(6); + + video::SColor white(255, 255, 255, 255); + v3f normal(0.f, 0.f, 1.f); + + m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); + m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); + m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); + m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); + + m_rotation_mesh_buffer.Indices[0] = 0; + m_rotation_mesh_buffer.Indices[1] = 1; + m_rotation_mesh_buffer.Indices[2] = 2; + m_rotation_mesh_buffer.Indices[3] = 2; + m_rotation_mesh_buffer.Indices[4] = 3; + m_rotation_mesh_buffer.Indices[5] = 0; + + m_rotation_mesh_buffer.getMaterial().Lighting = false; + m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; } Hud::~Hud() @@ -271,6 +298,18 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } } +bool Hud::hasElementOfType(HudElementType type) +{ + for (size_t i = 0; i != player->maxHudId(); i++) { + HudElement *e = player->getHud(i); + if (!e) + continue; + if (e->type == type) + return true; + } + return false; +} + // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false. bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos) { @@ -419,6 +458,69 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) core::rect<s32>(core::position2d<s32>(0,0), imgsize), NULL, colors, true); break; } + case HUD_ELEM_COMPASS: { + video::ITexture *texture = tsrc->getTexture(e->text); + if (!texture) + continue; + + // Positionning : + v2s32 dstsize(e->size.X, e->size.Y); + if (e->size.X < 0) + dstsize.X = m_screensize.X * (e->size.X * -0.01); + if (e->size.Y < 0) + dstsize.Y = m_screensize.Y * (e->size.Y * -0.01); + + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; // Avoid zero divides + + // Angle according to camera view + v3f fore(0.f, 0.f, 1.f); + scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera(); + cam->getAbsoluteTransformation().rotateVect(fore); + int angle = - fore.getHorizontalAngle().Y; + + // Limit angle and ajust with given offset + angle = (angle + (int)e->number) % 360; + + core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y); + dstrect += pos + v2s32( + (e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2) + + v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling); + + switch (e->dir) { + case HUD_COMPASS_ROTATE: + drawCompassRotate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_ROTATE_REVERSE: + drawCompassRotate(e, texture, dstrect, -angle); + break; + case HUD_COMPASS_TRANSLATE: + drawCompassTranslate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_TRANSLATE_REVERSE: + drawCompassTranslate(e, texture, dstrect, -angle); + break; + default: + break; + } + break; } + case HUD_ELEM_MINIMAP: { + if (e->size.X <= 0 || e->size.Y <= 0) + break; + if (!client->getMinimap()) + break; + // Draw a minimap of size "size" + v2s32 dstsize(e->size.X * m_scale_factor, + e->size.Y * m_scale_factor); + // (no percent size as minimap would likely be anamorphosed) + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2); + core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y); + rect += pos + offset + v2s32(e->offset.X * m_scale_factor, + e->offset.Y * m_scale_factor); + client->getMinimap()->drawMinimap(rect); + break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << " of hud element ID " << i << " due to unrecognized type" << std::endl; @@ -426,6 +528,74 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } } +void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int angle) +{ + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + + // Compute source image scaling + core::dimension2di imgsize(texture->getOriginalSize()); + core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height); + + v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height, + rect.getHeight() * e->scale.Y); + + // Avoid infinite loop + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; + + core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y); + tgtrect += v2s32( + (rect.getWidth() - dstsize.X) / 2, + (rect.getHeight() - dstsize.Y) / 2) + + rect.UpperLeftCorner; + + int offset = angle * dstsize.X / 360; + + tgtrect += v2s32(offset, 0); + + // Repeat image as much as needed + while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X) + tgtrect -= v2s32(dstsize.X, 0); + + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + + while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) { + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + } +} + +void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int angle) +{ + core::rect<s32> oldViewPort = driver->getViewPort(); + core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); + core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + + core::matrix4 Matrix; + Matrix.makeIdentity(); + Matrix.setRotationDegrees(v3f(0.f, 0.f, angle)); + + driver->setViewPort(rect); + driver->setTransform(video::ETS_PROJECTION, core::matrix4()); + driver->setTransform(video::ETS_VIEW, core::matrix4()); + driver->setTransform(video::ETS_WORLD, Matrix); + + video::SMaterial &material = m_rotation_mesh_buffer.getMaterial(); + material.TextureLayer[0].Texture = texture; + driver->setMaterial(material); + driver->drawMeshBuffer(&m_rotation_mesh_buffer); + + driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setTransform(video::ETS_VIEW, oldViewMat); + driver->setTransform(video::ETS_PROJECTION, oldProjMat); + + // restore the view area + driver->setViewPort(oldViewPort); +} void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, const std::string &bgtexture, @@ -601,6 +771,31 @@ void Hud::drawHotbar(u16 playeritem) { void Hud::drawCrosshair() { + if (pointing_at_object) { + if (use_object_crosshair_image) { + video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png"); + v2u32 size = object_crosshair->getOriginalSize(); + v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2), + m_displaycenter.Y - (size.Y / 2)); + driver->draw2DImage(object_crosshair, lsize, + core::rect<s32>(0, 0, size.X, size.Y), + nullptr, crosshair_argb, true); + } else { + driver->draw2DLine( + m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb); + driver->draw2DLine( + m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE, + -OBJECT_CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE, + OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb); + } + + return; + } + if (use_crosshair_image) { video::ITexture *crosshair = tsrc->getTexture("crosshair.png"); v2u32 size = crosshair->getOriginalSize(); @@ -608,12 +803,12 @@ void Hud::drawCrosshair() m_displaycenter.Y - (size.Y / 2)); driver->draw2DImage(crosshair, lsize, core::rect<s32>(0, 0, size.X, size.Y), - 0, crosshair_argb, true); + nullptr, crosshair_argb, true); } else { - driver->draw2DLine(m_displaycenter - v2s32(10, 0), - m_displaycenter + v2s32(10, 0), crosshair_argb); - driver->draw2DLine(m_displaycenter - v2s32(0, 10), - m_displaycenter + v2s32(0, 10), crosshair_argb); + driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0), + m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb); + driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE), + m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb); } } @@ -858,9 +1053,9 @@ void drawItemStack( if (def.type == ITEM_TOOL && item.wear != 0) { // Draw a progressbar - float barheight = rect.getHeight() / 16; - float barpad_x = rect.getWidth() / 16; - float barpad_y = rect.getHeight() / 16; + float barheight = static_cast<float>(rect.getHeight()) / 16; + float barpad_x = static_cast<float>(rect.getWidth()) / 16; + float barpad_y = static_cast<float>(rect.getHeight()) / 16; core::rect<s32> progressrect( rect.UpperLeftCorner.X + barpad_x, diff --git a/src/client/hud.h b/src/client/hud.h index 6f4c54626..d46545d71 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -45,12 +45,16 @@ public: video::SColor crosshair_argb; video::SColor selectionbox_argb; + bool use_crosshair_image = false; + bool use_object_crosshair_image = false; std::string hotbar_image = ""; bool use_hotbar_image = false; std::string hotbar_selected_image = ""; bool use_hotbar_selected_image = false; + bool pointing_at_object = false; + Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, Inventory *inventory); ~Hud(); @@ -77,6 +81,8 @@ public: m_selected_face_normal = face_normal; } + bool hasElementOfType(HudElementType type); + void drawLuaElements(const v3s16 &camera_offset); private: @@ -91,6 +97,12 @@ private: void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected); + void drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int way); + + void drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int way); + float m_hud_scaling; // cached minetest setting float m_scale_factor; v3s16 m_camera_offset; @@ -111,6 +123,8 @@ private: video::SMaterial m_selection_material; + scene::SMeshBuffer m_rotation_mesh_buffer; + enum { HIGHLIGHT_BOX, diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index a79b04a90..978baa320 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -37,6 +37,8 @@ void KeyCache::populate() key[KeyType::JUMP] = getKeySetting("keymap_jump"); key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); + key[KeyType::DIG] = getKeySetting("keymap_dig"); + key[KeyType::PLACE] = getKeySetting("keymap_place"); key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); @@ -112,56 +114,75 @@ bool MyEventReceiver::OnEvent(const SEvent &event) const KeyPress &keyCode = event.KeyInput; if (keysListenedFor[keyCode]) { if (event.KeyInput.PressedDown) { + if (!IsKeyDown(keyCode)) + keyWasPressed.set(keyCode); + keyIsDown.set(keyCode); keyWasDown.set(keyCode); } else { + if (IsKeyDown(keyCode)) + keyWasReleased.set(keyCode); + keyIsDown.unset(keyCode); } + return true; } - } #ifdef HAVE_TOUCHSCREENGUI - // case of touchscreengui we have to handle different events - if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + } else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + // In case of touchscreengui, we have to handle different events m_touchscreengui->translateEvent(event); return true; - } #endif - if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + } else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { /* TODO add a check like: if (event.JoystickEvent != joystick_we_listen_for) return false; */ return joystick->handleEvent(event.JoystickEvent); - } - // handle mouse events - if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { - if (isMenuActive()) { - left_active = false; - middle_active = false; - right_active = false; - } else { - left_active = event.MouseInput.isLeftPressed(); - middle_active = event.MouseInput.isMiddlePressed(); - right_active = event.MouseInput.isRightPressed(); - - if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { - leftclicked = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { - rightclicked = true; - } - if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { - leftreleased = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { - rightreleased = true; - } - if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) { - mouse_wheel += event.MouseInput.Wheel; - } + } else if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { + // Handle mouse events + KeyPress key; + switch (event.MouseInput.Event) { + case EMIE_LMOUSE_PRESSED_DOWN: + key = "KEY_LBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_MMOUSE_PRESSED_DOWN: + key = "KEY_MBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_RMOUSE_PRESSED_DOWN: + key = "KEY_RBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_LMOUSE_LEFT_UP: + key = "KEY_LBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MMOUSE_LEFT_UP: + key = "KEY_MBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_RMOUSE_LEFT_UP: + key = "KEY_RBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MOUSE_WHEEL: + mouse_wheel += event.MouseInput.Wheel; + break; + default: break; } } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) { static const LogLevel irr_loglev_conv[] = { @@ -188,38 +209,28 @@ s32 RandomInputHandler::Rand(s32 min, s32 max) return (myrand() % (max - min + 1)) + min; } +struct RandomInputHandlerSimData { + std::string key; + float counter; + int time_max; +}; + void RandomInputHandler::step(float dtime) { - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_jump")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_special1")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_forward")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_left")); + static RandomInputHandlerSimData rnd_data[] = { + { "keymap_jump", 0.0f, 40 }, + { "keymap_special1", 0.0f, 40 }, + { "keymap_forward", 0.0f, 40 }, + { "keymap_left", 0.0f, 40 }, + { "keymap_dig", 0.0f, 30 }, + { "keymap_place", 0.0f, 15 } + }; + + for (auto &i : rnd_data) { + i.counter -= dtime; + if (i.counter < 0.0) { + i.counter = 0.1 * Rand(1, i.time_max); + keydown.toggle(getKeySetting(i.key.c_str())); } } { @@ -230,29 +241,5 @@ void RandomInputHandler::step(float dtime) mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20)); } } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 30); - leftdown = !leftdown; - if (leftdown) - leftclicked = true; - if (!leftdown) - leftreleased = true; - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 15); - rightdown = !rightdown; - if (rightdown) - rightclicked = true; - if (!rightdown) - rightreleased = true; - } - } mousepos += mousespeed; } diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 9be2a2ed4..1fb4cf0ec 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -144,6 +144,14 @@ public: return b; } + // Checks whether a key was just pressed. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed[keycode]; } + + // Checks whether a key was just released. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased[keycode]; } + void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); } void dontListenForKeys() { keysListenedFor.clear(); } @@ -158,17 +166,20 @@ public: { keyIsDown.clear(); keyWasDown.clear(); + keyWasPressed.clear(); + keyWasReleased.clear(); - leftclicked = false; - rightclicked = false; - leftreleased = false; - rightreleased = false; + mouse_wheel = 0; + } - left_active = false; - middle_active = false; - right_active = false; + void clearWasKeyPressed() + { + keyWasPressed.clear(); + } - mouse_wheel = 0; + void clearWasKeyReleased() + { + keyWasReleased.clear(); } MyEventReceiver() @@ -178,15 +189,6 @@ public: #endif } - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; - - bool left_active = false; - bool middle_active = false; - bool right_active = false; - s32 mouse_wheel = 0; JoystickController *joystick = nullptr; @@ -198,8 +200,16 @@ public: private: // The current state of keys KeyList keyIsDown; - // Whether a key has been pressed or not + + // Like keyIsDown but only reset when that key is read KeyList keyWasDown; + + // Whether a key has just been pressed + KeyList keyWasPressed; + + // Whether a key has just been released + KeyList keyWasReleased; + // List of keys we listen for // TODO perhaps the type of this is not really // performant as KeyList is designed for few but @@ -219,29 +229,26 @@ public: virtual ~InputHandler() = default; + virtual bool isRandom() const + { + return false; + } + virtual bool isKeyDown(GameKeyType k) = 0; virtual bool wasKeyDown(GameKeyType k) = 0; + virtual bool wasKeyPressed(GameKeyType k) = 0; + virtual bool wasKeyReleased(GameKeyType k) = 0; virtual bool cancelPressed() = 0; + virtual void clearWasKeyPressed() {} + virtual void clearWasKeyReleased() {} + virtual void listenForKey(const KeyPress &keyCode) {} virtual void dontListenForKeys() {} virtual v2s32 getMousePos() = 0; virtual void setMousePos(s32 x, s32 y) = 0; - virtual bool getLeftState() = 0; - virtual bool getRightState() = 0; - - virtual bool getLeftClicked() = 0; - virtual bool getRightClicked() = 0; - virtual void resetLeftClicked() = 0; - virtual void resetRightClicked() = 0; - - virtual bool getLeftReleased() = 0; - virtual bool getRightReleased() = 0; - virtual void resetLeftReleased() = 0; - virtual void resetRightReleased() = 0; - virtual s32 getMouseWheel() = 0; virtual void step(float dtime) {} @@ -270,10 +277,26 @@ public: { return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k); } + virtual bool wasKeyPressed(GameKeyType k) + { + return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyPressed(k); + } + virtual bool wasKeyReleased(GameKeyType k) + { + return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k); + } virtual bool cancelPressed() { return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey); } + virtual void clearWasKeyPressed() + { + m_receiver->clearWasKeyPressed(); + } + virtual void clearWasKeyReleased() + { + m_receiver->clearWasKeyReleased(); + } virtual void listenForKey(const KeyPress &keyCode) { m_receiver->listenForKey(keyCode); @@ -301,59 +324,6 @@ public: } } - virtual bool getLeftState() - { - return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightState() - { - return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftClicked() - { - return m_receiver->leftclicked || - joystick.getWasKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightClicked() - { - return m_receiver->rightclicked || - joystick.getWasKeyDown(KeyType::MOUSE_R); - } - - virtual void resetLeftClicked() - { - m_receiver->leftclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_L); - } - virtual void resetRightClicked() - { - m_receiver->rightclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftReleased() - { - return m_receiver->leftreleased || - joystick.wasKeyReleased(KeyType::MOUSE_L); - } - virtual bool getRightReleased() - { - return m_receiver->rightreleased || - joystick.wasKeyReleased(KeyType::MOUSE_R); - } - - virtual void resetLeftReleased() - { - m_receiver->leftreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_L); - } - virtual void resetRightReleased() - { - m_receiver->rightreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_R); - } - virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } void clear() @@ -372,25 +342,19 @@ class RandomInputHandler : public InputHandler public: RandomInputHandler() = default; + bool isRandom() const + { + return true; + } + virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; } virtual bool wasKeyDown(GameKeyType k) { return false; } + virtual bool wasKeyPressed(GameKeyType k) { return false; } + virtual bool wasKeyReleased(GameKeyType k) { return false; } virtual bool cancelPressed() { return false; } virtual v2s32 getMousePos() { return mousepos; } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } - virtual bool getLeftState() { return leftdown; } - virtual bool getRightState() { return rightdown; } - - virtual bool getLeftClicked() { return leftclicked; } - virtual bool getRightClicked() { return rightclicked; } - virtual void resetLeftClicked() { leftclicked = false; } - virtual void resetRightClicked() { rightclicked = false; } - - virtual bool getLeftReleased() { return leftreleased; } - virtual bool getRightReleased() { return rightreleased; } - virtual void resetLeftReleased() { leftreleased = false; } - virtual void resetRightReleased() { rightreleased = false; } - virtual s32 getMouseWheel() { return 0; } virtual void step(float dtime); @@ -401,10 +365,4 @@ private: KeyList keydown; v2s32 mousepos; v2s32 mousespeed; - bool leftdown = false; - bool rightdown = false; - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; }; diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index c29e8b639..f61ae4ae6 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -37,7 +37,7 @@ bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const { s16 ax_val = ev.Axis[axis_to_compare]; - return (ax_val * direction < 0) && (thresh * direction > ax_val * direction); + return (ax_val * direction < -thresh); } // spares many characters @@ -48,7 +48,7 @@ JoystickLayout create_default_layout() { JoystickLayout jlo; - jlo.axes_dead_border = 1024; + jlo.axes_deadzone = g_settings->getU16("joystick_deadzone"); const JoystickAxisLayout axes[JA_COUNT] = { {0, 1}, // JA_SIDEWARD_MOVE @@ -74,8 +74,8 @@ JoystickLayout create_default_layout() // Accessible without four modifier button pressed // regardless whether start is pressed or not - JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4); - JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5); + JLO_B_PB(KeyType::DIG, fb | 1 << 4, 1 << 4); + JLO_B_PB(KeyType::PLACE, fb | 1 << 5, 1 << 5); // Accessible without any modifier pressed JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); @@ -83,9 +83,9 @@ JoystickLayout create_default_layout() // Accessible with start button not pressed, but four pressed // TODO find usage for button 0 - JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); - JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4); - JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5); + JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); + JLO_B_PB(KeyType::HOTBAR_PREV, bm | 1 << 4, fb | 1 << 4); + JLO_B_PB(KeyType::HOTBAR_NEXT, bm | 1 << 5, fb | 1 << 5); // Accessible with start button and four pressed // TODO find usage for buttons 0, 1 and 4, 5 @@ -93,14 +93,14 @@ JoystickLayout create_default_layout() // Now about the buttons simulated by the axes // Movement buttons, important for vessels - JLO_A_PB(KeyType::FORWARD, 1, 1, 1024); - JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024); - JLO_A_PB(KeyType::LEFT, 0, 1, 1024); - JLO_A_PB(KeyType::RIGHT, 0, -1, 1024); + JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone); + JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone); + JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone); + JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone); // Scroll buttons - JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024); - JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024); + JLO_A_PB(KeyType::HOTBAR_PREV, 2, -1, jlo.axes_deadzone); + JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, jlo.axes_deadzone); return jlo; } @@ -109,7 +109,7 @@ JoystickLayout create_xbox_layout() { JoystickLayout jlo; - jlo.axes_dead_border = 7000; + jlo.axes_deadzone = 7000; const JoystickAxisLayout axes[JA_COUNT] = { {0, 1}, // JA_SIDEWARD_MOVE @@ -134,10 +134,10 @@ JoystickLayout create_xbox_layout() JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right // Triggers - JLO_B_PB(KeyType::MOUSE_L, 1 << 6, 1 << 6); // lt - JLO_B_PB(KeyType::MOUSE_R, 1 << 7, 1 << 7); // rt - JLO_B_PB(KeyType::SCROLL_UP, 1 << 4, 1 << 4); // lb - JLO_B_PB(KeyType::SCROLL_DOWN, 1 << 5, 1 << 5); // rb + JLO_B_PB(KeyType::DIG, 1 << 6, 1 << 6); // lt + JLO_B_PB(KeyType::PLACE, 1 << 7, 1 << 7); // rt + JLO_B_PB(KeyType::HOTBAR_PREV, 1 << 4, 1 << 4); // lb + JLO_B_PB(KeyType::HOTBAR_NEXT, 1 << 5, 1 << 5); // rb // D-PAD JLO_B_PB(KeyType::ZOOM, 1 << 15, 1 << 15); // up @@ -146,10 +146,10 @@ JoystickLayout create_xbox_layout() JLO_B_PB(KeyType::FREEMOVE, 1 << 16, 1 << 16); // down // Movement buttons, important for vessels - JLO_A_PB(KeyType::FORWARD, 1, 1, 1024); - JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024); - JLO_A_PB(KeyType::LEFT, 0, 1, 1024); - JLO_A_PB(KeyType::RIGHT, 0, -1, 1024); + JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone); + JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone); + JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone); + JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone); return jlo; } @@ -219,16 +219,19 @@ bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev) for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { if (keys_pressed[i]) { - if (!m_past_pressed_keys[i] && + if (!m_past_keys_pressed[i] && m_past_pressed_time[i] < m_internal_time - doubling_dtime) { - m_past_pressed_keys[i] = true; + m_past_keys_pressed[i] = true; m_past_pressed_time[i] = m_internal_time; } - } else if (m_pressed_keys[i]) { - m_past_released_keys[i] = true; + } else if (m_keys_down[i]) { + m_keys_released[i] = true; } - m_pressed_keys[i] = keys_pressed[i]; + if (keys_pressed[i] && !(m_keys_down[i])) + m_keys_pressed[i] = true; + + m_keys_down[i] = keys_pressed[i]; } for (size_t i = 0; i < JA_COUNT; i++) { @@ -236,23 +239,22 @@ bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev) m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id]; } - return true; } void JoystickController::clear() { - m_pressed_keys.reset(); - m_past_pressed_keys.reset(); - m_past_released_keys.reset(); + m_keys_pressed.reset(); + m_keys_down.reset(); + m_past_keys_pressed.reset(); + m_keys_released.reset(); memset(m_axes_vals, 0, sizeof(m_axes_vals)); } s16 JoystickController::getAxisWithoutDead(JoystickAxis axis) { s16 v = m_axes_vals[axis]; - if (((v > 0) && (v < m_layout.axes_dead_border)) || - ((v < 0) && (v > -m_layout.axes_dead_border))) + if (abs(v) < m_layout.axes_deadzone) return 0; return v; } diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index 7baacd81b..3f361e4ef 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -96,7 +96,7 @@ struct JoystickLayout { std::vector<JoystickButtonCmb> button_keys; std::vector<JoystickAxisCmb> axis_keys; JoystickAxisLayout axes[JA_COUNT]; - s16 axes_dead_border; + s16 axes_deadzone; }; class JoystickController { @@ -111,37 +111,32 @@ public: bool wasKeyDown(GameKeyType b) { - bool r = m_past_pressed_keys[b]; - m_past_pressed_keys[b] = false; + bool r = m_past_keys_pressed[b]; + m_past_keys_pressed[b] = false; return r; } - bool getWasKeyDown(GameKeyType b) + + bool wasKeyReleased(GameKeyType b) { - return m_past_pressed_keys[b]; + return m_keys_released[b]; } - void clearWasKeyDown(GameKeyType b) + void clearWasKeyReleased(GameKeyType b) { - m_past_pressed_keys[b] = false; + m_keys_released[b] = false; } - bool wasKeyReleased(GameKeyType b) + bool wasKeyPressed(GameKeyType b) { - bool r = m_past_released_keys[b]; - m_past_released_keys[b] = false; - return r; - } - bool getWasKeyReleased(GameKeyType b) - { - return m_past_pressed_keys[b]; + return m_keys_pressed[b]; } - void clearWasKeyReleased(GameKeyType b) + void clearWasKeyPressed(GameKeyType b) { - m_past_pressed_keys[b] = false; + m_keys_pressed[b] = false; } bool isKeyDown(GameKeyType b) { - return m_pressed_keys[b]; + return m_keys_down[b]; } s16 getAxis(JoystickAxis axis) @@ -162,12 +157,13 @@ private: u8 m_joystick_id = 0; - std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_pressed_keys; + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_down; + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_pressed; f32 m_internal_time; f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT]; - std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_pressed_keys; - std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_released_keys; + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_keys_pressed; + std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_released; }; diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp index 6a0e9f569..ce5214f54 100644 --- a/src/client/keycode.cpp +++ b/src/client/keycode.cpp @@ -316,7 +316,8 @@ KeyPress::KeyPress(const char *name) int chars_read = mbtowc(&Char, name, 1); FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character"); m_name = ""; - warningstream << "KeyPress: Unknown key '" << name << "', falling back to first char."; + warningstream << "KeyPress: Unknown key '" << name + << "', falling back to first char." << std::endl; } KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character) diff --git a/src/client/keys.h b/src/client/keys.h index 50d3d194b..60a7a3c45 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -35,6 +35,8 @@ public: SPECIAL1, SNEAK, AUTOFORWARD, + DIG, + PLACE, ESC, @@ -108,12 +110,6 @@ public: SLOT_31, SLOT_32, - // joystick specific keys - MOUSE_L, - MOUSE_R, - SCROLL_UP, - SCROLL_DOWN, - // Fake keycode for array size and internal checks INTERNAL_ENUM_COUNT diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 1e7040d57..f3eb1a2dd 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include <cmath> -#include "event.h" +#include "mtevent.h" #include "collision.h" #include "nodedef.h" #include "settings.h" diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 1020e35f5..167e1e3ec 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -35,11 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc., MeshMakeData */ -MeshMakeData::MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices): +MeshMakeData::MeshMakeData(Client *client, bool use_shaders): m_client(client), - m_use_shaders(use_shaders), - m_use_tangent_vertices(use_tangent_vertices) + m_use_shaders(use_shaders) {} void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) @@ -81,33 +79,6 @@ void MeshMakeData::fill(MapBlock *block) } } -void MeshMakeData::fillSingleNode(MapNode *node) -{ - m_blockpos = v3s16(0,0,0); - - v3s16 blockpos_nodes = v3s16(0,0,0); - VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE, - blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)); - s32 volume = area.getVolume(); - s32 our_node_index = area.index(1,1,1); - - // Allocate this block + neighbors - m_vmanip.clear(); - m_vmanip.addArea(area); - - // Fill in data - MapNode *data = new MapNode[volume]; - for(s32 i = 0; i < volume; i++) - { - if (i == our_node_index) - data[i] = *node; - else - data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0); - } - m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent()); - delete[] data; -} - void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos) { if (crack_level >= 0) @@ -419,12 +390,21 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs) u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; idx = (idx - 1) * 4; +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ > 7 +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif +#endif memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16)); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif } static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v) { - if (dir.X > 0 || dir.Y > 0 || dir.Z < 0) + if (dir.X > 0 || dir.Y != 0 || dir.Z < 0) base -= scale; if (dir == v3s16(0,0,1)) { *u = -base.X - 1; @@ -442,8 +422,8 @@ static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, f *u = base.X + 1; *v = -base.Z - 2; } else if (dir == v3s16(0,-1,0)) { - *u = base.X; - *v = base.Z; + *u = base.X + 1; + *v = base.Z + 1; } } @@ -1034,10 +1014,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): for (auto &m : m_mesh) m = new scene::SMesh(); m_enable_shaders = data->m_use_shaders; - m_use_tangent_vertices = data->m_use_tangent_vertices; m_enable_vbo = g_settings->getBool("enable_vbo"); - if (g_settings->getBool("enable_minimap")) { + if (data->m_client->getMinimap()) { m_minimap_mapblock = new MinimapMapblock; m_minimap_mapblock->getMinimapNodes( &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE); @@ -1188,51 +1167,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer]; - // Create meshbuffer, add to mesh - if (m_use_tangent_vertices) { - scene::SMeshBufferTangents *buf = - new scene::SMeshBufferTangents(); - buf->Material = material; - buf->Vertices.reallocate(p.vertices.size()); - buf->Indices.reallocate(p.indices.size()); - for (const video::S3DVertex &v: p.vertices) - buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords)); - for (u16 i: p.indices) - buf->Indices.push_back(i); - buf->recalculateBoundingBox(); - mesh->addMeshBuffer(buf); - buf->drop(); - } else { - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Material = material; - buf->append(&p.vertices[0], p.vertices.size(), - &p.indices[0], p.indices.size()); - mesh->addMeshBuffer(buf); - buf->drop(); - } - } - - /* - Do some stuff to the mesh - */ - m_camera_offset = camera_offset; - translateMesh(m_mesh[layer], - intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); - - if (m_use_tangent_vertices) { - scene::IMeshManipulator* meshmanip = - RenderingEngine::get_scene_manager()->getMeshManipulator(); - meshmanip->recalculateTangents(m_mesh[layer], true, false, false); + scene::SMeshBuffer *buf = new scene::SMeshBuffer(); + buf->Material = material; + buf->append(&p.vertices[0], p.vertices.size(), + &p.indices[0], p.indices.size()); + mesh->addMeshBuffer(buf); + buf->drop(); } if (m_mesh[layer]) { -#if 0 - // Usually 1-700 faces and 1-7 materials - std::cout << "Updated MapBlock has " << fastfaces_new.size() - << " faces and uses " << m_mesh[layer]->getMeshBufferCount() - << " materials (meshbuffers)" << std::endl; -#endif - // Use VBO for mesh (this just would set this for ever buffer) if (m_enable_vbo) m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC); @@ -1251,13 +1194,13 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): MapBlockMesh::~MapBlockMesh() { for (scene::IMesh *m : m_mesh) { - if (m_enable_vbo && m) + if (m_enable_vbo) { for (u32 i = 0; i < m->getMeshBufferCount(); i++) { scene::IMeshBuffer *buf = m->getMeshBuffer(i); RenderingEngine::get_video_driver()->removeHardwareBuffer(buf); } + } m->drop(); - m = NULL; } delete m_minimap_mapblock; } @@ -1351,19 +1294,6 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, return true; } -void MapBlockMesh::updateCameraOffset(v3s16 camera_offset) -{ - if (camera_offset != m_camera_offset) { - for (scene::IMesh *layer : m_mesh) { - translateMesh(layer, - intToFloat(m_camera_offset - camera_offset, BS)); - if (m_enable_vbo) - layer->setDirty(); - } - m_camera_offset = camera_offset; - } -} - video::SColor encode_light(u16 light, u8 emissive_light) { // Get components diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 6af23a656..3b17c4af9 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -45,10 +45,8 @@ struct MeshMakeData Client *m_client; bool m_use_shaders; - bool m_use_tangent_vertices; - MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices = false); + MeshMakeData(Client *client, bool use_shaders); /* Copy block data manually (to allow optimizations by the caller) @@ -63,11 +61,6 @@ struct MeshMakeData void fill(MapBlock *block); /* - Set up with only a single node at (1,1,1) - */ - void fillSingleNode(MapNode *node); - - /* Set the (node) position of a crack */ void setCrack(int crack_level, v3s16 crack_pos); @@ -132,8 +125,6 @@ public: m_animation_force_timer--; } - void updateCameraOffset(v3s16 camera_offset); - private: scene::IMesh *m_mesh[MAX_TILE_LAYERS]; MinimapMapblock *m_minimap_mapblock; @@ -141,7 +132,6 @@ private: IShaderSource *m_shdrsrc; bool m_enable_shaders; - bool m_use_tangent_vertices; bool m_enable_vbo; // Must animate() be called before rendering? @@ -168,9 +158,6 @@ private: // of sunlit vertices // Keys are pairs of (mesh index, buffer index in the mesh) std::map<std::pair<u8, u32>, std::map<u32, video::SColor > > m_daynight_diffs; - - // Camera offset info -> do we have to translate the mesh? - v3s16 m_camera_offset; }; /*! diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index e1ec22068..2400a374c 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -203,6 +203,15 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) setMeshBufferColor(mesh->getMeshBuffer(j), color); } +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) +{ + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + assert(buf->getVertexCount() >= count); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < count; i++) + ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i]; +} + template <typename F> static void applyToMesh(scene::IMesh *mesh, const F &fn) { diff --git a/src/client/mesh.h b/src/client/mesh.h index 103c61e45..dbc091a06 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -58,6 +58,13 @@ void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color); */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); + +/* + Sets texture coords for vertices in the mesh buffer. + `uv[]` must have `count` elements +*/ +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count); + /* Set a constant color for an animated mesh */ diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 53b980eeb..c8d1cba26 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -52,9 +52,6 @@ MeshUpdateQueue::MeshUpdateQueue(Client *client): m_client(client) { m_cache_enable_shaders = g_settings->getBool("enable_shaders"); - m_cache_use_tangent_vertices = m_cache_enable_shaders && ( - g_settings->getBool("enable_bumpmapping") || - g_settings->getBool("enable_parallax_occlusion")); m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size"); } @@ -207,8 +204,7 @@ CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p) void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q) { - MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders, - m_cache_use_tangent_vertices); + MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders); q->data = data; data->fillBlockDataBegin(q->p); diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index 9a42852a3..4371b8390 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -40,7 +40,6 @@ struct QueuedMeshUpdate { v3s16 p = v3s16(-1337, -1337, -1337); bool ack_block_to_server = false; - bool urgent = false; int crack_level = -1; v3s16 crack_pos; MeshMakeData *data = nullptr; // This is generated in MeshUpdateQueue::pop() @@ -88,7 +87,6 @@ private: // TODO: Add callback to update these when g_settings changes bool m_cache_enable_shaders; - bool m_cache_use_tangent_vertices; bool m_cache_smooth_lighting; int m_meshgen_block_cache_size; diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 68770ec19..dd810ee0a 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "shader.h" #include "mapblock.h" #include "client/renderingengine.h" - +#include "gettext.h" //// //// MinimapUpdateThread @@ -108,8 +108,11 @@ void MinimapUpdateThread::doUpdate() } } - if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) { - getMap(data->pos, data->map_size, data->scan_height); + + if (data->map_invalidated && ( + data->mode.type == MINIMAP_TYPE_RADAR || + data->mode.type == MINIMAP_TYPE_SURFACE)) { + getMap(data->pos, data->mode.map_size, data->mode.scan_height); data->map_invalidated = false; } } @@ -181,19 +184,26 @@ Minimap::Minimap(Client *client) this->m_ndef = client->getNodeDefManager(); m_angle = 0.f; + m_current_mode_index = 0; // Initialize static settings m_enable_shaders = g_settings->getBool("enable_shaders"); m_surface_mode_scan_height = g_settings->getBool("minimap_double_scan_height") ? 256 : 128; + // Initialize minimap modes + addMode(MINIMAP_TYPE_OFF); + addMode(MINIMAP_TYPE_SURFACE, 256); + addMode(MINIMAP_TYPE_SURFACE, 128); + addMode(MINIMAP_TYPE_SURFACE, 64); + addMode(MINIMAP_TYPE_RADAR, 512); + addMode(MINIMAP_TYPE_RADAR, 256); + addMode(MINIMAP_TYPE_RADAR, 128); + // Initialize minimap data data = new MinimapData; - data->mode = MINIMAP_MODE_OFF; - data->is_radar = false; - data->map_invalidated = true; - data->texture = NULL; - data->heightmap_texture = NULL; + data->map_invalidated = true; + data->minimap_shape_round = g_settings->getBool("minimap_shape_round"); // Get round minimap textures @@ -215,6 +225,8 @@ Minimap::Minimap(Client *client) // Create object marker texture data->object_marker_red = m_tsrc->getTexture("object_marker_red.png"); + setModeIndex(0); + // Create mesh buffer for minimap m_meshbuffer = getMinimapMeshBuffer(); @@ -240,6 +252,10 @@ Minimap::~Minimap() driver->removeTexture(data->minimap_overlay_square); driver->removeTexture(data->object_marker_red); + for (MinimapMarker *m : m_markers) + delete m; + m_markers.clear(); + delete data; delete m_minimap_update_thread; } @@ -280,29 +296,102 @@ MinimapShape Minimap::getMinimapShape() return MINIMAP_SHAPE_SQUARE; } -void Minimap::setMinimapMode(MinimapMode mode) +void Minimap::setModeIndex(size_t index) { - static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = { - {false, 0, 0}, - {false, m_surface_mode_scan_height, 256}, - {false, m_surface_mode_scan_height, 128}, - {false, m_surface_mode_scan_height, 64}, - {true, 32, 128}, - {true, 32, 64}, - {true, 32, 32} - }; - - if (mode >= MINIMAP_MODE_COUNT) - return; - MutexAutoLock lock(m_mutex); - data->is_radar = modedefs[mode].is_radar; - data->scan_height = modedefs[mode].scan_height; - data->map_size = modedefs[mode].map_size; - data->mode = mode; + if (index < m_modes.size()) { + data->mode = m_modes[index]; + m_current_mode_index = index; + } else { + data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, gettext("Minimap hidden"), 0, 0, ""}; + m_current_mode_index = 0; + } - m_minimap_update_thread->deferUpdate(); + data->map_invalidated = true; + + if (m_minimap_update_thread) + m_minimap_update_thread->deferUpdate(); +} + +void Minimap::addMode(MinimapModeDef mode) +{ + // Check validity + if (mode.type == MINIMAP_TYPE_TEXTURE) { + if (mode.texture.empty()) + return; + if (mode.scale < 1) + mode.scale = 1; + } + + int zoom = -1; + + // Build a default standard label + if (mode.label == "") { + switch (mode.type) { + case MINIMAP_TYPE_OFF: + mode.label = gettext("Minimap hidden"); + break; + case MINIMAP_TYPE_SURFACE: + mode.label = gettext("Minimap in surface mode, Zoom x%d"); + if (mode.map_size > 0) + zoom = 256 / mode.map_size; + break; + case MINIMAP_TYPE_RADAR: + mode.label = gettext("Minimap in radar mode, Zoom x%d"); + if (mode.map_size > 0) + zoom = 512 / mode.map_size; + break; + case MINIMAP_TYPE_TEXTURE: + mode.label = gettext("Minimap in texture mode"); + break; + default: + break; + } + } + // else: Custom labels need mod-provided client-side translation + + if (zoom >= 0) { + char label_buf[1024]; + porting::mt_snprintf(label_buf, sizeof(label_buf), + mode.label.c_str(), zoom); + mode.label = label_buf; + } + + m_modes.push_back(mode); +} + +void Minimap::addMode(MinimapType type, u16 size, std::string label, + std::string texture, u16 scale) +{ + MinimapModeDef mode; + mode.type = type; + mode.label = label; + mode.map_size = size; + mode.texture = texture; + mode.scale = scale; + switch (type) { + case MINIMAP_TYPE_SURFACE: + mode.scan_height = m_surface_mode_scan_height; + break; + case MINIMAP_TYPE_RADAR: + mode.scan_height = 32; + break; + default: + mode.scan_height = 0; + } + addMode(mode); +} + +void Minimap::nextMode() +{ + if (m_modes.empty()) + return; + m_current_mode_index++; + if (m_current_mode_index >= m_modes.size()) + m_current_mode_index = 0; + + setModeIndex(m_current_mode_index); } void Minimap::setPos(v3s16 pos) @@ -331,16 +420,16 @@ void Minimap::setAngle(f32 angle) void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image) { video::SColor c(240, 0, 0, 0); - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; if (mmpixel->air_count > 0) c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255)); else c.setGreen(0); - map_image->setPixel(x, data->map_size - z - 1, c); + map_image->setPixel(x, data->mode.map_size - z - 1, c); } } @@ -349,9 +438,9 @@ void Minimap::blitMinimapPixelsToImageSurface( { // This variable creation/destruction has a 1% cost on rendering minimap video::SColor tilecolor; - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; const ContentFeatures &f = m_ndef->get(mmpixel->n); const TileDef *tile = &f.tiledef[0]; @@ -367,10 +456,10 @@ void Minimap::blitMinimapPixelsToImageSurface( tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); tilecolor.setAlpha(240); - map_image->setPixel(x, data->map_size - z - 1, tilecolor); + map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor); u32 h = mmpixel->height; - heightmap_image->setPixel(x,data->map_size - z - 1, + heightmap_image->setPixel(x,data->mode.map_size - z - 1, video::SColor(255, h, h, h)); } } @@ -378,21 +467,46 @@ void Minimap::blitMinimapPixelsToImageSurface( video::ITexture *Minimap::getMinimapTexture() { // update minimap textures when new scan is ready - if (data->map_invalidated) + if (data->map_invalidated && data->mode.type != MINIMAP_TYPE_TEXTURE) return data->texture; // create minimap and heightmap images in memory - core::dimension2d<u32> dim(data->map_size, data->map_size); + core::dimension2d<u32> dim(data->mode.map_size, data->mode.map_size); video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8, core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY)); // Blit MinimapPixels to images - if (data->is_radar) - blitMinimapPixelsToImageRadar(map_image); - else + switch(data->mode.type) { + case MINIMAP_TYPE_OFF: + break; + case MINIMAP_TYPE_SURFACE: blitMinimapPixelsToImageSurface(map_image, heightmap_image); + break; + case MINIMAP_TYPE_RADAR: + blitMinimapPixelsToImageRadar(map_image); + break; + case MINIMAP_TYPE_TEXTURE: + // Want to use texture source, to : 1 find texture, 2 cache it + video::ITexture* texture = m_tsrc->getTexture(data->mode.texture); + video::IImage* image = driver->createImageFromData( + texture->getColorFormat(), texture->getSize(), texture->lock(), true, false); + texture->unlock(); + + auto dim = image->getDimension(); + + map_image->fill(video::SColor(255, 0, 0, 0)); + + image->copyTo(map_image, + irr::core::vector2d<int> { + ((data->mode.map_size - (static_cast<int>(dim.Width))) >> 1) + - data->pos.X / data->mode.scale, + ((data->mode.map_size - (static_cast<int>(dim.Height))) >> 1) + + data->pos.Z / data->mode.scale + }); + image->drop(); + } map_image->copyToScaling(minimap_image); map_image->drop(); @@ -461,21 +575,31 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() void Minimap::drawMinimap() { + // Non hud managed minimap drawing (legacy minimap) + v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); + const u32 size = 0.25 * screensize.Y; + + drawMinimap(core::rect<s32>( + screensize.X - size - 10, 10, + screensize.X - 10, size + 10)); +} + +void Minimap::drawMinimap(core::rect<s32> rect) { + video::ITexture *minimap_texture = getMinimapTexture(); if (!minimap_texture) return; + if (data->mode.type == MINIMAP_TYPE_OFF) + return; + updateActiveMarkers(); - v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); - const u32 size = 0.25 * screensize.Y; core::rect<s32> oldViewPort = driver->getViewPort(); core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); - driver->setViewPort(core::rect<s32>( - screensize.X - size - 10, 10, - screensize.X - 10, size + 10)); + driver->setViewPort(rect); driver->setTransform(video::ETS_PROJECTION, core::matrix4()); driver->setTransform(video::ETS_VIEW, core::matrix4()); @@ -488,8 +612,8 @@ void Minimap::drawMinimap() material.TextureLayer[0].Texture = minimap_texture; material.TextureLayer[1].Texture = data->heightmap_texture; - if (m_enable_shaders && !data->is_radar) { - u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1); + if (m_enable_shaders && data->mode.type == MINIMAP_TYPE_SURFACE) { + u16 sid = m_shdrsrc->getShader("minimap_shader", TILE_MATERIAL_ALPHA); material.MaterialType = m_shdrsrc->getShaderInfo(sid).material; } else { material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -529,14 +653,14 @@ void Minimap::drawMinimap() driver->setViewPort(oldViewPort); // Draw player markers - v2s32 s_pos(screensize.X - size - 10, 10); + v2s32 s_pos(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y); core::dimension2di imgsize(data->object_marker_red->getOriginalSize()); core::rect<s32> img_rect(0, 0, imgsize.Width, imgsize.Height); static const video::SColor col(255, 255, 255, 255); static const video::SColor c[4] = {col, col, col, col}; f32 sin_angle = std::sin(m_angle * core::DEGTORAD); f32 cos_angle = std::cos(m_angle * core::DEGTORAD); - s32 marker_size2 = 0.025 * (float)size; + s32 marker_size2 = 0.025 * (float)rect.getWidth();; for (std::list<v2f>::const_iterator i = m_active_markers.begin(); i != m_active_markers.end(); ++i) { @@ -547,8 +671,8 @@ void Minimap::drawMinimap() posf.X = t1; posf.Y = t2; } - posf.X = (posf.X + 0.5) * (float)size; - posf.Y = (posf.Y + 0.5) * (float)size; + posf.X = (posf.X + 0.5) * (float)rect.getWidth(); + posf.Y = (posf.Y + 0.5) * (float)rect.getHeight(); core::rect<s32> dest_rect( s_pos.X + posf.X - marker_size2, s_pos.Y + posf.Y - marker_size2, @@ -559,28 +683,41 @@ void Minimap::drawMinimap() } } +MinimapMarker* Minimap::addMarker(scene::ISceneNode *parent_node) +{ + MinimapMarker *m = new MinimapMarker(parent_node); + m_markers.push_back(m); + return m; +} + +void Minimap::removeMarker(MinimapMarker **m) +{ + m_markers.remove(*m); + delete *m; + *m = nullptr; +} + void Minimap::updateActiveMarkers() { video::IImage *minimap_mask = data->minimap_shape_round ? data->minimap_mask_round : data->minimap_mask_square; - const std::list<Nametag *> &nametags = client->getCamera()->getNametags(); - m_active_markers.clear(); - - for (Nametag *nametag : nametags) { - v3s16 pos = floatToInt(nametag->parent_node->getAbsolutePosition() + - intToFloat(client->getCamera()->getOffset(), BS), BS); - pos -= data->pos - v3s16(data->map_size / 2, - data->scan_height / 2, - data->map_size / 2); - if (pos.X < 0 || pos.X > data->map_size || - pos.Y < 0 || pos.Y > data->scan_height || - pos.Z < 0 || pos.Z > data->map_size) { + v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS); + v3s16 pos_offset = data->pos - v3s16(data->mode.map_size / 2, + data->mode.scan_height / 2, + data->mode.map_size / 2); + + for (MinimapMarker *marker : m_markers) { + v3s16 pos = floatToInt(marker->parent_node->getAbsolutePosition() + + cam_offset, BS) - pos_offset; + if (pos.X < 0 || pos.X > data->mode.map_size || + pos.Y < 0 || pos.Y > data->mode.scan_height || + pos.Z < 0 || pos.Z > data->mode.map_size) { continue; } - pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX; - pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY; + pos.X = ((float)pos.X / data->mode.map_size) * MINIMAP_MAX_SX; + pos.Z = ((float)pos.Z / data->mode.map_size) * MINIMAP_MAX_SY; const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z); if (!mask_col.getAlpha()) { continue; diff --git a/src/client/minimap.h b/src/client/minimap.h index 258d5330d..87c9668ee 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "../hud.h" #include "irrlichttypes_extrabloated.h" #include "util/thread.h" #include "voxel.h" @@ -33,28 +34,27 @@ class IShaderSource; #define MINIMAP_MAX_SX 512 #define MINIMAP_MAX_SY 512 -enum MinimapMode { - MINIMAP_MODE_OFF, - MINIMAP_MODE_SURFACEx1, - MINIMAP_MODE_SURFACEx2, - MINIMAP_MODE_SURFACEx4, - MINIMAP_MODE_RADARx1, - MINIMAP_MODE_RADARx2, - MINIMAP_MODE_RADARx4, - MINIMAP_MODE_COUNT, -}; - enum MinimapShape { MINIMAP_SHAPE_SQUARE, MINIMAP_SHAPE_ROUND, }; struct MinimapModeDef { - bool is_radar; + MinimapType type; + std::string label; u16 scan_height; u16 map_size; + std::string texture; + u16 scale; }; +struct MinimapMarker { + MinimapMarker(scene::ISceneNode *parent_node): + parent_node(parent_node) + { + } + scene::ISceneNode *parent_node; +}; struct MinimapPixel { //! The topmost node that the minimap displays. MapNode n; @@ -69,12 +69,9 @@ struct MinimapMapblock { }; struct MinimapData { - bool is_radar; - MinimapMode mode; + MinimapModeDef mode; v3s16 pos; v3s16 old_pos; - u16 scan_height; - u16 map_size; MinimapPixel minimap_scan[MINIMAP_MAX_SX * MINIMAP_MAX_SY]; bool map_invalidated; bool minimap_shape_round; @@ -127,12 +124,21 @@ public: v3s16 getPos() const { return data->pos; } void setAngle(f32 angle); f32 getAngle() const { return m_angle; } - void setMinimapMode(MinimapMode mode); - MinimapMode getMinimapMode() const { return data->mode; } void toggleMinimapShape(); void setMinimapShape(MinimapShape shape); MinimapShape getMinimapShape(); + void clearModes() { m_modes.clear(); }; + void addMode(MinimapModeDef mode); + void addMode(MinimapType type, u16 size = 0, std::string label = "", + std::string texture = "", u16 scale = 1); + + void setModeIndex(size_t index); + size_t getModeIndex() const { return m_current_mode_index; }; + size_t getMaxModeIndex() const { return m_modes.size() - 1; }; + void nextMode(); + + MinimapModeDef getModeDef() const { return data->mode; } video::ITexture *getMinimapTexture(); @@ -142,8 +148,12 @@ public: scene::SMeshBuffer *getMinimapMeshBuffer(); + MinimapMarker* addMarker(scene::ISceneNode *parent_node); + void removeMarker(MinimapMarker **marker); + void updateActiveMarkers(); void drawMinimap(); + void drawMinimap(core::rect<s32> rect); video::IVideoDriver *driver; Client* client; @@ -153,11 +163,14 @@ private: ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; const NodeDefManager *m_ndef; - MinimapUpdateThread *m_minimap_update_thread; + MinimapUpdateThread *m_minimap_update_thread = nullptr; scene::SMeshBuffer *m_meshbuffer; bool m_enable_shaders; + std::vector<MinimapModeDef> m_modes; + size_t m_current_mode_index; u16 m_surface_mode_scan_height; f32 m_angle; std::mutex m_mutex; + std::list<MinimapMarker*> m_markers; std::list<v2f> m_active_markers; }; diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index bf5aa6c2c..92a7137ea 100644 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@ -86,6 +86,7 @@ void RenderingCore::drawHUD() if (show_hud) { if (draw_crosshair) hud->drawCrosshair(); + hud->drawHotbar(client->getEnv().getLocalPlayer()->getWieldIndex()); hud->drawLuaElements(camera->getOffset()); camera->drawNametags(); diff --git a/src/client/render/factory.cpp b/src/client/render/factory.cpp index 30f9480fc..7fcec40dd 100644 --- a/src/client/render/factory.cpp +++ b/src/client/render/factory.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "factory.h" -#include <stdexcept> +#include "log.h" #include "plain.h" #include "anaglyph.h" #include "interlaced.h" @@ -45,5 +45,8 @@ RenderingCore *createRenderingCore(const std::string &stereo_mode, IrrlichtDevic return new RenderingCoreSideBySide(device, client, hud, true); if (stereo_mode == "crossview") return new RenderingCoreSideBySide(device, client, hud, false, true); - throw std::invalid_argument("Invalid rendering mode: " + stereo_mode); + + // fallback to plain renderer + errorstream << "Invalid rendering mode: " << stereo_mode << std::endl; + return new RenderingCorePlain(device, client, hud); } diff --git a/src/client/render/interlaced.cpp b/src/client/render/interlaced.cpp index 2aadadc17..ce8e92f21 100644 --- a/src/client/render/interlaced.cpp +++ b/src/client/render/interlaced.cpp @@ -36,7 +36,7 @@ void RenderingCoreInterlaced::initMaterial() mat.UseMipMaps = false; mat.ZBuffer = false; mat.ZWriteEnable = false; - u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC, 0); + u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC); mat.MaterialType = s->getShaderInfo(shader).material; for (int k = 0; k < 3; ++k) { mat.TextureLayer[k].AnisotropicFilter = false; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index f5aca8f58..99ff8c1ee 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -153,7 +153,7 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) RenderingEngine::~RenderingEngine() { core.reset(); - m_device->drop(); + m_device->closeDevice(); s_singleton = nullptr; } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index ee6079f7a..b3e4911f4 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iterator> #include "shader.h" #include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" #include "debug.h" #include "filesys.h" #include "util/container.h" @@ -37,6 +38,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "gamedef.h" #include "client/tile.h" +#include "config.h" + +#if ENABLE_GLES +#ifdef _IRR_COMPILE_WITH_OGLES1_ +#include <GLES/gl.h> +#else +#include <GLES2/gl2.h> +#endif +#else +#ifndef __APPLE__ +#include <GL/gl.h> +#else +#define GL_SILENCE_DEPRECATION +#include <OpenGL/gl.h> +#endif +#endif /* A cache from shader name to shader path @@ -173,19 +190,14 @@ private: class ShaderCallback : public video::IShaderConstantSetCallBack { - std::vector<IShaderConstantSetter*> m_setters; + std::vector<std::unique_ptr<IShaderConstantSetter>> m_setters; public: - ShaderCallback(const std::vector<IShaderConstantSetterFactory *> &factories) + template <typename Factories> + ShaderCallback(const Factories &factories) { - for (IShaderConstantSetterFactory *factory : factories) - m_setters.push_back(factory->create()); - } - - ~ShaderCallback() - { - for (IShaderConstantSetter *setter : m_setters) - delete setter; + for (auto &&factory : factories) + m_setters.push_back(std::unique_ptr<IShaderConstantSetter>(factory->create())); } virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override @@ -193,15 +205,13 @@ public: video::IVideoDriver *driver = services->getVideoDriver(); sanity_check(driver != NULL); - bool is_highlevel = userData; - - for (IShaderConstantSetter *setter : m_setters) - setter->onSetConstants(services, is_highlevel); + for (auto &&setter : m_setters) + setter->onSetConstants(services); } virtual void OnSetMaterial(const video::SMaterial& material) override { - for (IShaderConstantSetter *setter : m_setters) + for (auto &&setter : m_setters) setter->onSetMaterial(material); } }; @@ -215,37 +225,61 @@ class MainShaderConstantSetter : public IShaderConstantSetter { CachedVertexShaderSetting<float, 16> m_world_view_proj; CachedVertexShaderSetting<float, 16> m_world; +#if ENABLE_GLES + // Modelview matrix + CachedVertexShaderSetting<float, 16> m_world_view; + // Texture matrix + CachedVertexShaderSetting<float, 16> m_texture; + // Normal matrix + CachedVertexShaderSetting<float, 9> m_normal; +#endif public: MainShaderConstantSetter() : - m_world_view_proj("mWorldViewProj"), - m_world("mWorld") + m_world_view_proj("mWorldViewProj") + , m_world("mWorld") +#if ENABLE_GLES + , m_world_view("mWorldView") + , m_texture("mTexture") + , m_normal("mNormal") +#endif {} ~MainShaderConstantSetter() = default; - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) + virtual void onSetConstants(video::IMaterialRendererServices *services) override { video::IVideoDriver *driver = services->getVideoDriver(); sanity_check(driver); - // Set clip matrix - core::matrix4 worldViewProj; - worldViewProj = driver->getTransform(video::ETS_PROJECTION); - worldViewProj *= driver->getTransform(video::ETS_VIEW); - worldViewProj *= driver->getTransform(video::ETS_WORLD); - if (is_highlevel) - m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services); - else - services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4); - // Set world matrix core::matrix4 world = driver->getTransform(video::ETS_WORLD); - if (is_highlevel) - m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services); - else - services->setVertexShaderConstant(world.pointer(), 4, 4); + m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services); + + // Set clip matrix + core::matrix4 worldView; + worldView = driver->getTransform(video::ETS_VIEW); + worldView *= world; + core::matrix4 worldViewProj; + worldViewProj = driver->getTransform(video::ETS_PROJECTION); + worldViewProj *= worldView; + m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services); + +#if ENABLE_GLES + core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0); + m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services); + m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services); + + core::matrix4 normal; + worldView.getTransposed(normal); + sanity_check(normal.makeInverse()); + float m[9] = { + normal[0], normal[1], normal[2], + normal[4], normal[5], normal[6], + normal[8], normal[9], normal[10], + }; + m_normal.set(m, services); +#endif } }; @@ -266,7 +300,6 @@ class ShaderSource : public IWritableShaderSource { public: ShaderSource(); - ~ShaderSource(); /* - If shader material specified by name is found from cache, @@ -276,7 +309,7 @@ public: The id 0 points to a null shader. Its material is EMT_SOLID. */ u32 getShaderIdDirect(const std::string &name, - const u8 material_type, const u8 drawtype); + MaterialType material_type, NodeDrawType drawtype) override; /* If shader specified by the name pointed by the id doesn't @@ -288,26 +321,26 @@ public: */ u32 getShader(const std::string &name, - const u8 material_type, const u8 drawtype); + MaterialType material_type, NodeDrawType drawtype) override; - ShaderInfo getShaderInfo(u32 id); + ShaderInfo getShaderInfo(u32 id) override; // Processes queued shader requests from other threads. // Shall be called from the main thread. - void processQueue(); + void processQueue() override; // Insert a shader program into the cache without touching the // filesystem. Shall be called from the main thread. void insertSourceShader(const std::string &name_of_shader, - const std::string &filename, const std::string &program); + const std::string &filename, const std::string &program) override; // Rebuild shaders from the current set of source shaders // Shall be called from the main thread. - void rebuildShaders(); + void rebuildShaders() override; - void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) + void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) override { - m_setter_factories.push_back(setter); + m_setter_factories.push_back(std::unique_ptr<IShaderConstantSetterFactory>(setter)); } private: @@ -329,10 +362,11 @@ private: RequestQueue<std::string, u32, u8, u8> m_get_shader_queue; // Global constant setter factories - std::vector<IShaderConstantSetterFactory *> m_setter_factories; + std::vector<std::unique_ptr<IShaderConstantSetterFactory>> m_setter_factories; - // Shader callbacks - std::vector<ShaderCallback *> m_callbacks; + // Generate shader given the shader name. + ShaderInfo generateShader(const std::string &name, + MaterialType material_type, NodeDrawType drawtype); }; IWritableShaderSource *createShaderSource() @@ -340,22 +374,6 @@ IWritableShaderSource *createShaderSource() return new ShaderSource(); } -/* - Generate shader given the shader name. -*/ -ShaderInfo generate_shader(const std::string &name, - u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks, - const std::vector<IShaderConstantSetterFactory *> &setter_factories, - SourceShaderCache *sourcecache); - -/* - Load shader programs -*/ -void load_shaders(const std::string &name, SourceShaderCache *sourcecache, - video::E_DRIVER_TYPE drivertype, bool enable_shaders, - std::string &vertex_program, std::string &pixel_program, - std::string &geometry_program, bool &is_highlevel); - ShaderSource::ShaderSource() { m_main_thread = std::this_thread::get_id(); @@ -367,18 +385,8 @@ ShaderSource::ShaderSource() addShaderConstantSetterFactory(new MainShaderConstantSetterFactory()); } -ShaderSource::~ShaderSource() -{ - for (ShaderCallback *callback : m_callbacks) { - delete callback; - } - for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) { - delete setter_factorie; - } -} - u32 ShaderSource::getShader(const std::string &name, - const u8 material_type, const u8 drawtype) + MaterialType material_type, NodeDrawType drawtype) { /* Get shader @@ -420,7 +428,7 @@ u32 ShaderSource::getShader(const std::string &name, This method generates all the shaders */ u32 ShaderSource::getShaderIdDirect(const std::string &name, - const u8 material_type, const u8 drawtype) + MaterialType material_type, NodeDrawType drawtype) { //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl; @@ -447,8 +455,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name, return 0; } - ShaderInfo info = generate_shader(name, material_type, drawtype, - m_callbacks, m_setter_factories, &m_sourcecache); + ShaderInfo info = generateShader(name, material_type, drawtype); /* Add shader to caches (add dummy shaders too) @@ -512,24 +519,19 @@ void ShaderSource::rebuildShaders() for (ShaderInfo &i : m_shaderinfo_cache) { ShaderInfo *info = &i; if (!info->name.empty()) { - *info = generate_shader(info->name, info->material_type, - info->drawtype, m_callbacks, - m_setter_factories, &m_sourcecache); + *info = generateShader(info->name, info->material_type, info->drawtype); } } } -ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype, - std::vector<ShaderCallback *> &callbacks, - const std::vector<IShaderConstantSetterFactory *> &setter_factories, - SourceShaderCache *sourcecache) +ShaderInfo ShaderSource::generateShader(const std::string &name, + MaterialType material_type, NodeDrawType drawtype) { ShaderInfo shaderinfo; shaderinfo.name = name; shaderinfo.material_type = material_type; shaderinfo.drawtype = drawtype; - shaderinfo.material = video::EMT_SOLID; switch (material_type) { case TILE_MATERIAL_OPAQUE: case TILE_MATERIAL_LIQUID_OPAQUE: @@ -550,326 +552,171 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; } + shaderinfo.material = shaderinfo.base_material; bool enable_shaders = g_settings->getBool("enable_shaders"); if (!enable_shaders) return shaderinfo; video::IVideoDriver *driver = RenderingEngine::get_video_driver(); - - video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices(); - if(!gpu){ - errorstream<<"generate_shader(): " - "failed to generate \""<<name<<"\", " - "GPU programming not supported." - <<std::endl; + if (!driver->queryFeature(video::EVDF_ARB_GLSL)) { + errorstream << "Shaders are enabled but GLSL is not supported by the driver\n"; return shaderinfo; } - - // Choose shader language depending on driver type and settings - // Then load shaders - std::string vertex_program; - std::string pixel_program; - std::string geometry_program; - bool is_highlevel; - load_shaders(name, sourcecache, driver->getDriverType(), - enable_shaders, vertex_program, pixel_program, - geometry_program, is_highlevel); - // Check hardware/driver support - if (!vertex_program.empty() && - !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) && - !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){ - infostream<<"generate_shader(): vertex shaders disabled " - "because of missing driver/hardware support." - <<std::endl; - vertex_program = ""; - } - if (!pixel_program.empty() && - !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) && - !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){ - infostream<<"generate_shader(): pixel shaders disabled " - "because of missing driver/hardware support." - <<std::endl; - pixel_program = ""; - } - if (!geometry_program.empty() && - !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){ - infostream<<"generate_shader(): geometry shaders disabled " - "because of missing driver/hardware support." - <<std::endl; - geometry_program = ""; - } - - // If no shaders are used, don't make a separate material type - if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty()) - return shaderinfo; + video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices(); // Create shaders header - std::string shaders_header = "#version 120\n"; - - static const char* drawTypes[] = { - "NDT_NORMAL", - "NDT_AIRLIKE", - "NDT_LIQUID", - "NDT_FLOWINGLIQUID", - "NDT_GLASSLIKE", - "NDT_ALLFACES", - "NDT_ALLFACES_OPTIONAL", - "NDT_TORCHLIKE", - "NDT_SIGNLIKE", - "NDT_PLANTLIKE", - "NDT_FENCELIKE", - "NDT_RAILLIKE", - "NDT_NODEBOX", - "NDT_GLASSLIKE_FRAMED", - "NDT_FIRELIKE", - "NDT_GLASSLIKE_FRAMED_OPTIONAL", - "NDT_PLANTLIKE_ROOTED", - }; - - for (int i = 0; i < 14; i++){ - shaders_header += "#define "; - shaders_header += drawTypes[i]; - shaders_header += " "; - shaders_header += itos(i); - shaders_header += "\n"; - } - - static const char* materialTypes[] = { - "TILE_MATERIAL_BASIC", - "TILE_MATERIAL_ALPHA", - "TILE_MATERIAL_LIQUID_TRANSPARENT", - "TILE_MATERIAL_LIQUID_OPAQUE", - "TILE_MATERIAL_WAVING_LEAVES", - "TILE_MATERIAL_WAVING_PLANTS", - "TILE_MATERIAL_OPAQUE", - "TILE_MATERIAL_WAVING_LIQUID_BASIC", - "TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT", - "TILE_MATERIAL_WAVING_LIQUID_OPAQUE", - "TILE_MATERIAL_PLAIN", - "TILE_MATERIAL_PLAIN_ALPHA", - }; - - for (int i = 0; i < 12; i++){ - shaders_header += "#define "; - shaders_header += materialTypes[i]; - shaders_header += " "; - shaders_header += itos(i); - shaders_header += "\n"; - } - - shaders_header += "#define MATERIAL_TYPE "; - shaders_header += itos(material_type); - shaders_header += "\n"; - shaders_header += "#define DRAW_TYPE "; - shaders_header += itos(drawtype); - shaders_header += "\n"; - - if (g_settings->getBool("generate_normalmaps")) { - shaders_header += "#define GENERATE_NORMALMAPS 1\n"; + bool use_gles = false; +#if ENABLE_GLES + use_gles = driver->getDriverType() == video::EDT_OGLES2; +#endif + std::stringstream shaders_header; + shaders_header + << std::noboolalpha + << std::showpoint // for GLSL ES + ; + std::string vertex_header, fragment_header, geometry_header; + if (use_gles) { + shaders_header << R"( + #version 100 + )"; + vertex_header = R"( + uniform highp mat4 mWorldView; + uniform highp mat4 mWorldViewProj; + uniform mediump mat4 mTexture; + uniform mediump mat3 mNormal; + + attribute highp vec4 inVertexPosition; + attribute lowp vec4 inVertexColor; + attribute mediump vec4 inTexCoord0; + attribute mediump vec3 inVertexNormal; + attribute mediump vec4 inVertexTangent; + attribute mediump vec4 inVertexBinormal; + )"; + fragment_header = R"( + precision mediump float; + )"; } else { - shaders_header += "#define GENERATE_NORMALMAPS 0\n"; - } - shaders_header += "#define NORMALMAPS_STRENGTH "; - shaders_header += ftos(g_settings->getFloat("normalmaps_strength")); - shaders_header += "\n"; - float sample_step; - int smooth = (int)g_settings->getFloat("normalmaps_smooth"); - switch (smooth){ - case 0: - sample_step = 0.0078125; // 1.0 / 128.0 - break; - case 1: - sample_step = 0.00390625; // 1.0 / 256.0 - break; - case 2: - sample_step = 0.001953125; // 1.0 / 512.0 - break; - default: - sample_step = 0.0078125; - break; - } - shaders_header += "#define SAMPLE_STEP "; - shaders_header += ftos(sample_step); - shaders_header += "\n"; - - if (g_settings->getBool("enable_bumpmapping")) - shaders_header += "#define ENABLE_BUMPMAPPING\n"; - - if (g_settings->getBool("enable_parallax_occlusion")){ - int mode = g_settings->getFloat("parallax_occlusion_mode"); - float scale = g_settings->getFloat("parallax_occlusion_scale"); - float bias = g_settings->getFloat("parallax_occlusion_bias"); - int iterations = g_settings->getFloat("parallax_occlusion_iterations"); - shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n"; - shaders_header += "#define PARALLAX_OCCLUSION_MODE "; - shaders_header += itos(mode); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_SCALE "; - shaders_header += ftos(scale); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_BIAS "; - shaders_header += ftos(bias); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS "; - shaders_header += itos(iterations); - shaders_header += "\n"; - } - - shaders_header += "#define USE_NORMALMAPS "; - if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion")) - shaders_header += "1\n"; - else - shaders_header += "0\n"; - - if (g_settings->getBool("enable_waving_water")){ - shaders_header += "#define ENABLE_WAVING_WATER 1\n"; - shaders_header += "#define WATER_WAVE_HEIGHT "; - shaders_header += ftos(g_settings->getFloat("water_wave_height")); - shaders_header += "\n"; - shaders_header += "#define WATER_WAVE_LENGTH "; - shaders_header += ftos(g_settings->getFloat("water_wave_length")); - shaders_header += "\n"; - shaders_header += "#define WATER_WAVE_SPEED "; - shaders_header += ftos(g_settings->getFloat("water_wave_speed")); - shaders_header += "\n"; - } else{ - shaders_header += "#define ENABLE_WAVING_WATER 0\n"; - } - - shaders_header += "#define ENABLE_WAVING_LEAVES "; - if (g_settings->getBool("enable_waving_leaves")) - shaders_header += "1\n"; - else - shaders_header += "0\n"; - - shaders_header += "#define ENABLE_WAVING_PLANTS "; - if (g_settings->getBool("enable_waving_plants")) - shaders_header += "1\n"; - else - shaders_header += "0\n"; - - if (g_settings->getBool("tone_mapping")) - shaders_header += "#define ENABLE_TONE_MAPPING\n"; - - shaders_header += "#define FOG_START "; - shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f)); - shaders_header += "\n"; - - // Call addHighLevelShaderMaterial() or addShaderMaterial() - const c8* vertex_program_ptr = 0; - const c8* pixel_program_ptr = 0; - const c8* geometry_program_ptr = 0; - if (!vertex_program.empty()) { - vertex_program = shaders_header + vertex_program; - vertex_program_ptr = vertex_program.c_str(); - } - if (!pixel_program.empty()) { - pixel_program = shaders_header + pixel_program; - pixel_program_ptr = pixel_program.c_str(); - } - if (!geometry_program.empty()) { - geometry_program = shaders_header + geometry_program; - geometry_program_ptr = geometry_program.c_str(); - } - ShaderCallback *cb = new ShaderCallback(setter_factories); - s32 shadermat = -1; - if(is_highlevel){ - infostream<<"Compiling high level shaders for "<<name<<std::endl; - shadermat = gpu->addHighLevelShaderMaterial( - vertex_program_ptr, // Vertex shader program - "vertexMain", // Vertex shader entry point - video::EVST_VS_1_1, // Vertex shader version - pixel_program_ptr, // Pixel shader program - "pixelMain", // Pixel shader entry point - video::EPST_PS_1_2, // Pixel shader version - geometry_program_ptr, // Geometry shader program - "geometryMain", // Geometry shader entry point - video::EGST_GS_4_0, // Geometry shader version - scene::EPT_TRIANGLES, // Geometry shader input - scene::EPT_TRIANGLE_STRIP, // Geometry shader output - 0, // Support maximum number of vertices - cb, // Set-constant callback - shaderinfo.base_material, // Base material - 1 // Userdata passed to callback - ); - if(shadermat == -1){ - errorstream<<"generate_shader(): " - "failed to generate \""<<name<<"\", " - "addHighLevelShaderMaterial failed." - <<std::endl; - dumpShaderProgram(warningstream, "Vertex", vertex_program); - dumpShaderProgram(warningstream, "Pixel", pixel_program); - dumpShaderProgram(warningstream, "Geometry", geometry_program); - delete cb; - return shaderinfo; - } - } - else{ - infostream<<"Compiling assembly shaders for "<<name<<std::endl; - shadermat = gpu->addShaderMaterial( - vertex_program_ptr, // Vertex shader program - pixel_program_ptr, // Pixel shader program - cb, // Set-constant callback - shaderinfo.base_material, // Base material - 0 // Userdata passed to callback - ); - - if(shadermat == -1){ - errorstream<<"generate_shader(): " - "failed to generate \""<<name<<"\", " - "addShaderMaterial failed." - <<std::endl; - dumpShaderProgram(warningstream, "Vertex", vertex_program); - dumpShaderProgram(warningstream,"Pixel", pixel_program); - delete cb; - return shaderinfo; - } + shaders_header << R"( + #version 120 + #define lowp + #define mediump + #define highp + )"; + vertex_header = R"( + #define mWorldView gl_ModelViewMatrix + #define mWorldViewProj gl_ModelViewProjectionMatrix + #define mTexture (gl_TextureMatrix[0]) + #define mNormal gl_NormalMatrix + + #define inVertexPosition gl_Vertex + #define inVertexColor gl_Color + #define inTexCoord0 gl_MultiTexCoord0 + #define inVertexNormal gl_Normal + #define inVertexTangent gl_MultiTexCoord1 + #define inVertexBinormal gl_MultiTexCoord2 + )"; + } + + bool use_discard = use_gles; +#ifdef __unix__ + // For renderers that should use discard instead of GL_ALPHA_TEST + const char* gl_renderer = (const char*)glGetString(GL_RENDERER); + if (strstr(gl_renderer, "GC7000")) + use_discard = true; +#endif + if (use_discard && shaderinfo.base_material != video::EMT_SOLID) + shaders_header << "#define USE_DISCARD 1\n"; + +#define PROVIDE(constant) shaders_header << "#define " #constant " " << (int)constant << "\n" + + PROVIDE(NDT_NORMAL); + PROVIDE(NDT_AIRLIKE); + PROVIDE(NDT_LIQUID); + PROVIDE(NDT_FLOWINGLIQUID); + PROVIDE(NDT_GLASSLIKE); + PROVIDE(NDT_ALLFACES); + PROVIDE(NDT_ALLFACES_OPTIONAL); + PROVIDE(NDT_TORCHLIKE); + PROVIDE(NDT_SIGNLIKE); + PROVIDE(NDT_PLANTLIKE); + PROVIDE(NDT_FENCELIKE); + PROVIDE(NDT_RAILLIKE); + PROVIDE(NDT_NODEBOX); + PROVIDE(NDT_GLASSLIKE_FRAMED); + PROVIDE(NDT_FIRELIKE); + PROVIDE(NDT_GLASSLIKE_FRAMED_OPTIONAL); + PROVIDE(NDT_PLANTLIKE_ROOTED); + + PROVIDE(TILE_MATERIAL_BASIC); + PROVIDE(TILE_MATERIAL_ALPHA); + PROVIDE(TILE_MATERIAL_LIQUID_TRANSPARENT); + PROVIDE(TILE_MATERIAL_LIQUID_OPAQUE); + PROVIDE(TILE_MATERIAL_WAVING_LEAVES); + PROVIDE(TILE_MATERIAL_WAVING_PLANTS); + PROVIDE(TILE_MATERIAL_OPAQUE); + PROVIDE(TILE_MATERIAL_WAVING_LIQUID_BASIC); + PROVIDE(TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT); + PROVIDE(TILE_MATERIAL_WAVING_LIQUID_OPAQUE); + PROVIDE(TILE_MATERIAL_PLAIN); + PROVIDE(TILE_MATERIAL_PLAIN_ALPHA); + +#undef PROVIDE + + shaders_header << "#define MATERIAL_TYPE " << (int)material_type << "\n"; + shaders_header << "#define DRAW_TYPE " << (int)drawtype << "\n"; + + bool enable_waving_water = g_settings->getBool("enable_waving_water"); + shaders_header << "#define ENABLE_WAVING_WATER " << enable_waving_water << "\n"; + if (enable_waving_water) { + shaders_header << "#define WATER_WAVE_HEIGHT " << g_settings->getFloat("water_wave_height") << "\n"; + shaders_header << "#define WATER_WAVE_LENGTH " << g_settings->getFloat("water_wave_length") << "\n"; + shaders_header << "#define WATER_WAVE_SPEED " << g_settings->getFloat("water_wave_speed") << "\n"; + } + + shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n"; + shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n"; + shaders_header << "#define ENABLE_TONE_MAPPING " << g_settings->getBool("tone_mapping") << "\n"; + + shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n"; + + std::string common_header = shaders_header.str(); + + std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl"); + std::string fragment_shader = m_sourcecache.getOrLoad(name, "opengl_fragment.glsl"); + std::string geometry_shader = m_sourcecache.getOrLoad(name, "opengl_geometry.glsl"); + + vertex_shader = common_header + vertex_header + vertex_shader; + fragment_shader = common_header + fragment_header + fragment_shader; + const char *geometry_shader_ptr = nullptr; // optional + if (!geometry_shader.empty()) { + geometry_shader = common_header + geometry_header + geometry_shader; + geometry_shader_ptr = geometry_shader.c_str(); + } + + irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)}; + infostream<<"Compiling high level shaders for "<<name<<std::endl; + s32 shadermat = gpu->addHighLevelShaderMaterial( + vertex_shader.c_str(), nullptr, video::EVST_VS_1_1, + fragment_shader.c_str(), nullptr, video::EPST_PS_1_1, + geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0, + cb.get(), shaderinfo.base_material, 1); + if (shadermat == -1) { + errorstream<<"generate_shader(): " + "failed to generate \""<<name<<"\", " + "addHighLevelShaderMaterial failed." + <<std::endl; + dumpShaderProgram(warningstream, "Vertex", vertex_shader); + dumpShaderProgram(warningstream, "Fragment", fragment_shader); + dumpShaderProgram(warningstream, "Geometry", geometry_shader); + return shaderinfo; } - callbacks.push_back(cb); - - // HACK, TODO: investigate this better - // Grab the material renderer once more so minetest doesn't crash on exit - driver->getMaterialRenderer(shadermat)->grab(); // Apply the newly created material type shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat; return shaderinfo; } -void load_shaders(const std::string &name, SourceShaderCache *sourcecache, - video::E_DRIVER_TYPE drivertype, bool enable_shaders, - std::string &vertex_program, std::string &pixel_program, - std::string &geometry_program, bool &is_highlevel) -{ - vertex_program = ""; - pixel_program = ""; - geometry_program = ""; - is_highlevel = false; - - if(enable_shaders){ - // Look for high level shaders - if(drivertype == video::EDT_DIRECT3D9){ - // Direct3D 9: HLSL - // (All shaders in one file) - vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl"); - pixel_program = vertex_program; - geometry_program = vertex_program; - } - else if(drivertype == video::EDT_OPENGL){ - // OpenGL: GLSL - vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl"); - pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl"); - geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl"); - } - if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){ - is_highlevel = true; - return; - } - } - -} - void dumpShaderProgram(std::ostream &output_stream, const std::string &program_type, const std::string &program) { diff --git a/src/client/shader.h b/src/client/shader.h index 109d39336..38ab76704 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -20,9 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include <IMaterialRendererServices.h> #include "irrlichttypes_bloated.h" +#include <IMaterialRendererServices.h> #include <string> +#include "tile.h" +#include "nodedef.h" class IGameDef; @@ -46,8 +48,8 @@ struct ShaderInfo { std::string name = ""; video::E_MATERIAL_TYPE base_material = video::EMT_SOLID; video::E_MATERIAL_TYPE material = video::EMT_SOLID; - u8 drawtype = 0; - u8 material_type = 0; + NodeDrawType drawtype = NDT_NORMAL; + MaterialType material_type = TILE_MATERIAL_BASIC; ShaderInfo() = default; virtual ~ShaderInfo() = default; @@ -65,8 +67,7 @@ namespace irr { namespace video { class IShaderConstantSetter { public: virtual ~IShaderConstantSetter() = default; - virtual void onSetConstants(video::IMaterialRendererServices *services, - bool is_highlevel) = 0; + virtual void onSetConstants(video::IMaterialRendererServices *services) = 0; virtual void onSetMaterial(const video::SMaterial& material) { } }; @@ -128,10 +129,10 @@ public: virtual ~IShaderSource() = default; virtual u32 getShaderIdDirect(const std::string &name, - const u8 material_type, const u8 drawtype){return 0;} + MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL){return 0;} virtual ShaderInfo getShaderInfo(u32 id){return ShaderInfo();} virtual u32 getShader(const std::string &name, - const u8 material_type, const u8 drawtype){return 0;} + MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL){return 0;} }; class IWritableShaderSource : public IShaderSource { @@ -139,16 +140,12 @@ public: IWritableShaderSource() = default; virtual ~IWritableShaderSource() = default; - virtual u32 getShaderIdDirect(const std::string &name, - const u8 material_type, const u8 drawtype){return 0;} - virtual ShaderInfo getShaderInfo(u32 id){return ShaderInfo();} - virtual u32 getShader(const std::string &name, - const u8 material_type, const u8 drawtype){return 0;} - virtual void processQueue()=0; virtual void insertSourceShader(const std::string &name_of_shader, const std::string &filename, const std::string &program)=0; virtual void rebuildShaders()=0; + + /// @note Takes ownership of @p setter. virtual void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) = 0; }; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 2e0cbca86..3a40321dd 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2020 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -34,16 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" using namespace irr::core; -Sky::Sky(s32 id, ITextureSource *tsrc) : - scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), - RenderingEngine::get_scene_manager(), id) +static video::SMaterial baseMaterial() { - setAutomaticCulling(scene::EAC_OFF); - m_box.MaxEdge.set(0, 0, 0); - m_box.MinEdge.set(0, 0, 0); - - // Create material - video::SMaterial mat; mat.Lighting = false; #if ENABLE_GLES @@ -56,14 +49,31 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; mat.BackfaceCulling = false; + return mat; +}; + +Sky::Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc) : + scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), + RenderingEngine::get_scene_manager(), id) +{ + setAutomaticCulling(scene::EAC_OFF); + m_box.MaxEdge.set(0, 0, 0); + m_box.MinEdge.set(0, 0, 0); + + m_enable_shaders = g_settings->getBool("enable_shaders"); - m_materials[0] = mat; + // Create materials - m_materials[1] = mat; + m_materials[0] = baseMaterial(); + m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material; + m_materials[0].Lighting = true; + m_materials[0].ColorMaterial = video::ECM_NONE; + + m_materials[1] = baseMaterial(); //m_materials[1].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m_materials[2] = mat; + m_materials[2] = baseMaterial(); m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png")); m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; //m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; @@ -80,7 +90,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : tsrc->getTexture(m_moon_params.tonemap) : NULL; if (m_sun_texture) { - m_materials[3] = mat; + m_materials[3] = baseMaterial(); m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; // Disables texture filtering @@ -92,7 +102,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : m_materials[3].Lighting = true; } if (m_moon_texture) { - m_materials[4] = mat; + m_materials[4] = baseMaterial(); m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; // Disables texture filtering @@ -105,7 +115,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : } for (int i = 5; i < 11; i++) { - m_materials[i] = mat; + m_materials[i] = baseMaterial(); m_materials[i].Lighting = true; m_materials[i].MaterialType = video::EMT_SOLID; } @@ -202,7 +212,7 @@ void Sky::render() const f32 t = 1.0f; const f32 o = 0.0f; - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[6] = {0, 1, 2, 0, 2, 3}; video::S3DVertex vertices[4]; driver->setMaterial(m_materials[1]); @@ -244,7 +254,7 @@ void Sky::render() vertex.Pos.rotateXZBy(180); } } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -270,7 +280,7 @@ void Sky::render() // Switch from -Z (south) to +Z (north) vertex.Pos.rotateXZBy(-180); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -301,7 +311,7 @@ void Sky::render() // Switch from -Z (south) to -X (west) vertex.Pos.rotateXZBy(-90); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } // Draw sun @@ -337,7 +347,7 @@ void Sky::render() // Switch from -Z (south) to +Z (north) vertex.Pos.rotateXZBy(-180); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } // Draw bottom far cloudy fog thing in front of sun, moon and stars @@ -346,7 +356,7 @@ void Sky::render() vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t); vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o); vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } } @@ -590,7 +600,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol * wicked_time_of_day: current time of day, to know where should be the sun in the sky */ { - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[] = {0, 1, 2, 0, 2, 3}; std::array<video::S3DVertex, 4> vertices; if (!m_sun_texture) { driver->setMaterial(m_materials[1]); @@ -608,7 +618,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol for (int i = 0; i < 4; i++) { draw_sky_body(vertices, -sunsizes[i], sunsizes[i], colors[i]); place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } else { driver->setMaterial(m_materials[3]); @@ -620,7 +630,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol c = video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -637,7 +647,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC * the sky */ { - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[] = {0, 1, 2, 0, 2, 3}; std::array<video::S3DVertex, 4> vertices; if (!m_moon_texture) { driver->setMaterial(m_materials[1]); @@ -661,7 +671,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC for (int i = 0; i < 4; i++) { draw_sky_body(vertices, moonsizes_1[i], moonsizes_2[i], colors[i]); place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } else { driver->setMaterial(m_materials[4]); @@ -673,13 +683,12 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC c = video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) { - driver->setMaterial(m_materials[1]); // Tune values so that stars first appear just after the sun // disappears over the horizon, and disappear just before the sun // appears over the horizon. @@ -687,87 +696,18 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) // to time 4000. float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day); - float starbrightness = clamp((0.25f - fabsf(tod)) * 20.0f, 0.0f, 1.0f); - - float f = starbrightness; - float d = (0.006 / 2) * m_star_params.scale; - - video::SColor starcolor = m_star_params.starcolor; - starcolor.setAlpha(f * m_star_params.starcolor.getAlpha()); - - // Stars are only drawn when not fully transparent - if (m_star_params.starcolor.getAlpha() < 1) + float starbrightness = (0.25f - fabsf(tod)) * 20.0f; + m_star_color = m_star_params.starcolor; + m_star_color.a *= clamp(starbrightness, 0.0f, 1.0f); + if (m_star_color.a <= 0.0f) // Stars are only drawn when not fully transparent return; -#if ENABLE_GLES - u16 *indices = new u16[m_star_params.count * 3]; - video::S3DVertex *vertices = - new video::S3DVertex[m_star_params.count * 3]; - for (u32 i = 0; i < m_star_params.count; i++) { - indices[i * 3 + 0] = i * 3 + 0; - indices[i * 3 + 1] = i * 3 + 1; - indices[i * 3 + 2] = i * 3 + 2; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, 0); - v3f p2 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 3 + 0].Pos = p; - vertices[i * 3 + 0].Color = starcolor; - vertices[i * 3 + 1].Pos = p1; - vertices[i * 3 + 1].Color = starcolor; - vertices[i * 3 + 2].Pos = p2; - vertices[i * 3 + 2].Color = starcolor; - } - driver->drawIndexedTriangleList(vertices, m_star_params.count * 3, - indices, m_star_params.count); - delete[] indices; - delete[] vertices; -#else - u16 *indices = new u16[m_star_params.count * 4]; - video::S3DVertex *vertices = - new video::S3DVertex[m_star_params.count * 4]; - for (u32 i = 0; i < m_star_params.count; i++) { - indices[i * 4 + 0] = i * 4 + 0; - indices[i * 4 + 1] = i * 4 + 1; - indices[i * 4 + 2] = i * 4 + 2; - indices[i * 4 + 3] = i * 4 + 3; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, -d); - v3f p2 = v3f(d, 1, d); - v3f p3 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - a.rotateVect(p3); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - p3.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 4 + 0].Pos = p; - vertices[i * 4 + 0].Color = starcolor; - vertices[i * 4 + 1].Pos = p1; - vertices[i * 4 + 1].Color = starcolor; - vertices[i * 4 + 2].Pos = p2; - vertices[i * 4 + 2].Color = starcolor; - vertices[i * 4 + 3].Pos = p3; - vertices[i * 4 + 3].Color = starcolor; - } - driver->drawVertexPrimitiveList(vertices, m_star_params.count * 4, - indices, m_star_params.count, video::EVT_STANDARD, - scene::EPT_QUADS, video::EIT_16BIT); - delete[] indices; - delete[] vertices; -#endif + m_materials[0].DiffuseColor = m_materials[0].EmissiveColor = m_star_color.toSColor(); + auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f)); + auto world_matrix = driver->getTransform(video::ETS_WORLD); + driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation); + driver->setMaterial(m_materials[0]); + driver->drawMeshBuffer(m_stars.get()); + driver->setTransform(video::ETS_WORLD, world_matrix); } void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, float pos_2, const video::SColor &c) @@ -822,7 +762,7 @@ void Sky::setSunTexture(std::string sun_texture, m_sun_texture = tsrc->getTextureForMesh(m_sun_params.texture); if (m_sun_texture) { - m_materials[3] = m_materials[0]; + m_materials[3] = baseMaterial(); m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video:: EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -870,7 +810,7 @@ void Sky::setMoonTexture(std::string moon_texture, m_moon_texture = tsrc->getTextureForMesh(m_moon_params.texture); if (m_moon_texture) { - m_materials[4] = m_materials[0]; + m_materials[4] = baseMaterial(); m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video:: EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -892,19 +832,58 @@ void Sky::setStarCount(u16 star_count, bool force_update) // Allow force updating star count at game init. if (m_star_params.count != star_count || force_update) { m_star_params.count = star_count; - m_stars.clear(); - // Rebuild the stars surrounding the camera - for (u16 i = 0; i < star_count; i++) { - v3f star = v3f( - myrand_range(-10000, 10000), - myrand_range(-10000, 10000), - myrand_range(-10000, 10000) - ); - - star.normalize(); - m_stars.emplace_back(star); - } + m_seed = (u64)myrand() << 32 | myrand(); + updateStars(); + } +} + +void Sky::updateStars() +{ + m_stars.reset(new scene::SMeshBuffer()); + // Stupid IrrLicht doesn’t allow non-indexed rendering, and indexed quad + // rendering is slow due to lack of hardware support. So as indices are + // 16-bit and there are 4 vertices per star... the limit is 2^16/4 = 0x4000. + // That should be well enough actually. + if (m_star_params.count > 0x4000) { + warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n"; + m_star_params.count = 0x4000; + } + m_stars->Vertices.reallocate(4 * m_star_params.count); + m_stars->Indices.reallocate(6 * m_star_params.count); + + video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” + PcgRandom rgen(m_seed); + float d = (0.006 / 2) * m_star_params.scale; + for (u16 i = 0; i < m_star_params.count; i++) { + v3f r = v3f( + rgen.range(-10000, 10000), + rgen.range(-10000, 10000), + rgen.range(-10000, 10000) + ); + core::CMatrix4<f32> a; + a.buildRotateFromTo(v3f(0, 1, 0), r); + v3f p = v3f(-d, 1, -d); + v3f p1 = v3f(d, 1, -d); + v3f p2 = v3f(d, 1, d); + v3f p3 = v3f(-d, 1, d); + a.rotateVect(p); + a.rotateVect(p1); + a.rotateVect(p2); + a.rotateVect(p3); + m_stars->Vertices.push_back(video::S3DVertex(p, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); + } + for (u16 i = 0; i < m_star_params.count; i++) { + m_stars->Indices.push_back(i * 4 + 0); + m_stars->Indices.push_back(i * 4 + 1); + m_stars->Indices.push_back(i * 4 + 2); + m_stars->Indices.push_back(i * 4 + 2); + m_stars->Indices.push_back(i * 4 + 3); + m_stars->Indices.push_back(i * 4 + 0); } + m_stars->setHardwareMappingHint(scene::EHM_STATIC); } void Sky::setSkyColors(const SkyColor &sky_color) @@ -936,7 +915,7 @@ void Sky::addTextureToSkybox(std::string texture, int material_id, // Keep a list of texture names handy. m_sky_params.textures.emplace_back(texture); video::ITexture *result = tsrc->getTextureForMesh(texture); - m_materials[material_id+5] = m_materials[0]; + m_materials[material_id+5] = baseMaterial(); m_materials[material_id+5].setTexture(0, result); m_materials[material_id+5].MaterialType = video::EMT_SOLID; } diff --git a/src/client/sky.h b/src/client/sky.h index 3227e8f59..342a97596 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -17,10 +17,12 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "irrlichttypes_extrabloated.h" #include <ISceneNode.h> #include <array> #include "camera.h" -#include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" +#include "shader.h" #include "skyparams.h" #pragma once @@ -34,7 +36,7 @@ class Sky : public scene::ISceneNode { public: //! constructor - Sky(s32 id, ITextureSource *tsrc); + Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc); virtual void OnRegisterSceneNode(); @@ -77,7 +79,7 @@ public: void setStarsVisible(bool stars_visible) { m_star_params.visible = stars_visible; } void setStarCount(u16 star_count, bool force_update); void setStarColor(video::SColor star_color) { m_star_params.starcolor = star_color; } - void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; } + void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; updateStars(); } bool getCloudsVisible() const { return m_clouds_visible && m_clouds_enabled; } const video::SColorf &getCloudColor() const { return m_cloudcolor_f; } @@ -101,6 +103,8 @@ public: void clearSkyboxTextures() { m_sky_params.textures.clear(); } void addTextureToSkybox(std::string texture, int material_id, ITextureSource *tsrc); + const video::SColorf &getCurrentStarColor() const { return m_star_color; } + private: aabb3f m_box; video::SMaterial m_materials[SKY_MATERIAL_COUNT]; @@ -154,6 +158,7 @@ private: bool m_clouds_enabled = true; // Initialised to true, reset only by set_sky API bool m_directional_colored_fog; bool m_in_clouds = true; // Prevent duplicating bools to remember old values + bool m_enable_shaders = false; video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); @@ -178,13 +183,17 @@ private: bool m_default_tint = true; - std::vector<v3f> m_stars; + u64 m_seed = 0; + irr_ptr<scene::SMeshBuffer> m_stars; + video::SColorf m_star_color; video::ITexture *m_sun_texture; video::ITexture *m_moon_texture; video::ITexture *m_sun_tonemap; video::ITexture *m_moon_tonemap; + void updateStars(); + void draw_sun(video::IVideoDriver *driver, float sunsize, const video::SColor &suncolor, const video::SColor &suncolor2, float wicked_time_of_day); void draw_moon(video::IVideoDriver *driver, float moonsize, const video::SColor &mooncolor, diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index 20a651c1d..f4e61f93e 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -28,6 +28,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc., #include <alc.h> //#include <alext.h> #elif defined(__APPLE__) + #define OPENAL_DEPRECATED #include <OpenAL/al.h> #include <OpenAL/alc.h> //#include <OpenAL/alext.h> @@ -337,14 +338,12 @@ private: }; std::unordered_map<int, FadeState> m_sounds_fading; - float m_fade_delay; public: OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher): m_fetcher(fetcher), m_device(smg->m_device.get()), m_context(smg->m_context.get()), - m_next_id(1), - m_fade_delay(0) + m_next_id(1) { infostream << "Audio: Initialized: OpenAL " << std::endl; } @@ -616,38 +615,45 @@ public: void fadeSound(int soundid, float step, float gain) { - m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain); + // Ignore the command if step isn't valid. + if (step == 0) + return; + float current_gain = getSoundGain(soundid); + step = gain - current_gain > 0 ? abs(step) : -abs(step); + if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) { + auto current_fade = m_sounds_fading[soundid]; + // Do not replace the fade if it's equivalent. + if (current_fade.target_gain == gain && current_fade.step == step) + return; + m_sounds_fading.erase(soundid); + } + gain = rangelim(gain, 0, 1); + m_sounds_fading[soundid] = FadeState(step, current_gain, gain); } void doFades(float dtime) { - m_fade_delay += dtime; - - if (m_fade_delay < 0.1f) - return; + for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) { + FadeState& fade = i->second; + assert(fade.step != 0); + fade.current_gain += (fade.step * dtime); - float chkGain = 0; - for (auto i = m_sounds_fading.begin(); - i != m_sounds_fading.end();) { - if (i->second.step < 0.f) - chkGain = -(i->second.current_gain); + if (fade.step < 0.f) + fade.current_gain = std::max(fade.current_gain, fade.target_gain); else - chkGain = i->second.current_gain; + fade.current_gain = std::min(fade.current_gain, fade.target_gain); - if (chkGain < i->second.target_gain) { - i->second.current_gain += (i->second.step * m_fade_delay); - i->second.current_gain = rangelim(i->second.current_gain, 0, 1); - - updateSoundGain(i->first, i->second.current_gain); - ++i; - } else { - if (i->second.target_gain <= 0.f) - stopSound(i->first); + if (fade.current_gain <= 0.f) + stopSound(i->first); + else + updateSoundGain(i->first, fade.current_gain); + // The increment must happen during the erase call, or else it'll segfault. + if (fade.current_gain == fade.target_gain) m_sounds_fading.erase(i++); - } + else + i++; } - m_fade_delay = 0; } bool soundExists(int sound) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index d03588b2b..f2639757e 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -429,7 +429,6 @@ private: // Cached settings needed for making textures from meshes bool m_setting_trilinear_filter; bool m_setting_bilinear_filter; - bool m_setting_anisotropic_filter; }; IWritableTextureSource *createTextureSource() @@ -450,7 +449,6 @@ TextureSource::TextureSource() // for these settings to take effect m_setting_trilinear_filter = g_settings->getBool("trilinear_filter"); m_setting_bilinear_filter = g_settings->getBool("bilinear_filter"); - m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter"); } TextureSource::~TextureSource() @@ -1633,6 +1631,13 @@ bool TextureSource::generateImagePart(std::string part_of_name, /* IMPORTANT: When changing this, getTextureForMesh() needs to be * updated too. */ + if (!baseimg) { + errorstream << "generateImagePart(): baseimg == NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + // Apply the "clean transparent" filter, if configured. if (g_settings->getBool("texture_clean_transparent")) imageCleanTransparent(baseimg, 127); @@ -2219,9 +2224,14 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name) video::IVideoDriver *driver = RenderingEngine::get_video_driver(); video::SColor c(0, 0, 0, 0); video::ITexture *texture = getTexture(name); + if (!texture) + return c; video::IImage *image = driver->createImage(texture, core::position2d<s32>(0, 0), texture->getOriginalSize()); + if (!image) + return c; + u32 total = 0; u32 tR = 0; u32 tG = 0; diff --git a/src/client/tile.h b/src/client/tile.h index 52e0a2b2b..49c46f749 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> #include <vector> #include <SMaterial.h> -#include <memory> #include "util/numeric.h" #include "config.h" @@ -272,7 +271,7 @@ struct TileLayer u32 texture_id = 0; u16 animation_frame_length_ms = 0; - u8 animation_frame_count = 1; + u16 animation_frame_count = 1; u8 material_type = TILE_MATERIAL_BASIC; u8 material_flags = @@ -284,7 +283,7 @@ struct TileLayer //! If true, the tile has its own color. bool has_color = false; - std::shared_ptr<std::vector<FrameSpec>> frames = nullptr; + std::vector<FrameSpec> *frames = nullptr; /*! * The color of the tile, or if the tile does not own diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 8cd3e29a9..ad583210a 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -303,13 +303,24 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors) +scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors, const ContentFeatures &f) { - MeshMakeData mesh_make_data(client, false, false); + MeshMakeData mesh_make_data(client, false); MeshCollector collector; mesh_make_data.setSmoothLighting(false); MapblockMeshGenerator gen(&mesh_make_data, &collector); - gen.renderSingle(id); + u8 param2 = 0; + if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { + if (f.drawtype == NDT_TORCHLIKE) + param2 = 1; + else if (f.drawtype == NDT_SIGNLIKE || + f.drawtype == NDT_NODEBOX || + f.drawtype == NDT_MESH) + param2 = 4; + } + gen.renderSingle(id, param2); + colors->clear(); scene::SMesh *mesh = new scene::SMesh(); for (auto &prebuffers : collector.prebuffers) @@ -319,8 +330,9 @@ scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<It p.layer.texture = frame.texture; p.layer.normal_texture = frame.normal_texture; } - for (video::S3DVertex &v : p.vertices) + for (video::S3DVertex &v : p.vertices) { v.Color.setAlpha(255); + } scene::SMeshBuffer *buf = new scene::SMeshBuffer(); buf->Material.setTexture(0, p.layer.texture); p.layer.applyMaterialOptions(buf->Material); @@ -368,73 +380,61 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che // Handle nodes // See also CItemDefManager::createClientCached() if (def.type == ITEM_NODE) { - if (f.mesh_ptr[0]) { - // e.g. mesh nodes and nodeboxes - mesh = cloneMesh(f.mesh_ptr[0]); - postProcessNodeMesh(mesh, f, m_enable_shaders, true, - &m_material_type, &m_colors); + bool cull_backface = f.needsBackfaceCulling(); + + // Select rendering method + switch (f.drawtype) { + case NDT_AIRLIKE: + setExtruded("no_texture_airlike.png", "", + v3f(1.0, 1.0, 1.0), tsrc, 1); + break; + case NDT_SIGNLIKE: + case NDT_TORCHLIKE: + case NDT_RAILLIKE: + case NDT_PLANTLIKE: + case NDT_PLANTLIKE_ROOTED: + case NDT_FLOWINGLIQUID: { + v3f wscale = def.wield_scale; + if (f.drawtype == NDT_FLOWINGLIQUID) + wscale.Z *= 0.1f; + setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + tsrc->getTextureName(f.tiles[0].layers[1].texture_id), + wscale, tsrc, + f.tiles[0].layers[0].animation_frame_count); + // Add color + const TileLayer &l0 = f.tiles[0].layers[0]; + m_colors.emplace_back(l0.has_color, l0.color); + const TileLayer &l1 = f.tiles[0].layers[1]; + m_colors.emplace_back(l1.has_color, l1.color); + break; + } + case NDT_NORMAL: + case NDT_ALLFACES: + case NDT_LIQUID: + setCube(f, def.wield_scale); + break; + default: + // Render non-trivial drawtypes like the actual node + mesh = createSpecialNodeMesh(client, id, &m_colors, f); changeToMesh(mesh); mesh->drop(); - // mesh is pre-scaled by BS * f->visual_scale m_meshnode->setScale( - def.wield_scale * WIELD_SCALE_FACTOR - / (BS * f.visual_scale)); - } else { - switch (f.drawtype) { - case NDT_AIRLIKE: { - changeToMesh(nullptr); - break; - } - case NDT_PLANTLIKE: { - setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id), - def.wield_scale, tsrc, - f.tiles[0].layers[0].animation_frame_count); - // Add color - const TileLayer &l0 = f.tiles[0].layers[0]; - m_colors.emplace_back(l0.has_color, l0.color); - const TileLayer &l1 = f.tiles[0].layers[1]; - m_colors.emplace_back(l1.has_color, l1.color); - break; - } - case NDT_PLANTLIKE_ROOTED: { - setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), - "", def.wield_scale, tsrc, - f.special_tiles[0].layers[0].animation_frame_count); - // Add color - const TileLayer &l0 = f.special_tiles[0].layers[0]; - m_colors.emplace_back(l0.has_color, l0.color); - break; - } - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: { - setCube(f, def.wield_scale); - break; - } - default: { - mesh = createSpecialNodeMesh(client, id, &m_colors); - changeToMesh(mesh); - mesh->drop(); - m_meshnode->setScale( - def.wield_scale * WIELD_SCALE_FACTOR - / (BS * f.visual_scale)); - } - } + def.wield_scale * WIELD_SCALE_FACTOR + / (BS * f.visual_scale)); + break; } + u32 material_count = m_meshnode->getMaterialCount(); for (u32 i = 0; i < material_count; ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); material.MaterialType = m_material_type; material.MaterialTypeParam = 0.5f; - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, cull_backface); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); } return; - } - else if (!def.inventory_image.empty()) { + } else if (!def.inventory_image.empty()) { setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, tsrc, 1); m_colors.emplace_back(); @@ -529,6 +529,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) // Shading is on by default result->needs_shading = true; + bool cull_backface = f.needsBackfaceCulling(); + // If inventory_image is defined, it overrides everything else if (!def.inventory_image.empty()) { mesh = getExtrudedMesh(tsrc, def.inventory_image, @@ -538,51 +540,56 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // Items with inventory images do not need shading result->needs_shading = false; + } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) { + // Fallback image for airlike node + mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", + def.inventory_overlay); + result->needs_shading = false; } else if (def.type == ITEM_NODE) { - if (f.mesh_ptr[0]) { - mesh = cloneMesh(f.mesh_ptr[0]); - scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); + switch (f.drawtype) { + case NDT_NORMAL: + case NDT_ALLFACES: + case NDT_LIQUID: + case NDT_FLOWINGLIQUID: { + scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); + mesh = cloneMesh(cube); + cube->drop(); + if (f.drawtype == NDT_FLOWINGLIQUID) { + scaleMesh(mesh, v3f(1.2, 0.03, 1.2)); + translateMesh(mesh, v3f(0, -0.57, 0)); + } else + scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); + // add overlays postProcessNodeMesh(mesh, f, false, false, nullptr, - &result->buffer_colors); - } else { - switch (f.drawtype) { - case NDT_PLANTLIKE: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); - // Add color - const TileLayer &l0 = f.tiles[0].layers[0]; - result->buffer_colors.emplace_back(l0.has_color, l0.color); - const TileLayer &l1 = f.tiles[0].layers[1]; - result->buffer_colors.emplace_back(l1.has_color, l1.color); - break; - } - case NDT_PLANTLIKE_ROOTED: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); - // Add color - const TileLayer &l0 = f.special_tiles[0].layers[0]; - result->buffer_colors.emplace_back(l0.has_color, l0.color); - break; - } - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: { - scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); - mesh = cloneMesh(cube); - cube->drop(); - scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); - // add overlays - postProcessNodeMesh(mesh, f, false, false, nullptr, - &result->buffer_colors); - break; - } - default: { - mesh = createSpecialNodeMesh(client, id, &result->buffer_colors); - scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - } - } + &result->buffer_colors, true); + if (f.drawtype == NDT_ALLFACES) + scaleMesh(mesh, v3f(f.visual_scale)); + break; + } + case NDT_PLANTLIKE: { + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); + // Add color + const TileLayer &l0 = f.tiles[0].layers[0]; + result->buffer_colors.emplace_back(l0.has_color, l0.color); + const TileLayer &l1 = f.tiles[0].layers[1]; + result->buffer_colors.emplace_back(l1.has_color, l1.color); + break; + } + case NDT_PLANTLIKE_ROOTED: { + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); + // Add color + const TileLayer &l0 = f.special_tiles[0].layers[0]; + result->buffer_colors.emplace_back(l0.has_color, l0.color); + break; + } + default: + // Render non-trivial drawtypes like the actual node + mesh = createSpecialNodeMesh(client, id, &result->buffer_colors, f); + scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); + break; } u32 mc = mesh->getMeshBufferCount(); @@ -593,7 +600,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) material.MaterialTypeParam = 0.5f; material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_TRILINEAR_FILTER, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, cull_backface); material.setFlag(video::EMF_LIGHTING, false); } |