diff options
Diffstat (limited to 'src/client')
39 files changed, 1700 insertions, 1721 deletions
diff --git a/src/client/activeobjectmgr.cpp b/src/client/activeobjectmgr.cpp index 4ed98d79b..82f3cb944 100644 --- a/src/client/activeobjectmgr.cpp +++ b/src/client/activeobjectmgr.cpp @@ -29,14 +29,16 @@ void ActiveObjectMgr::clear() // delete active objects for (auto &active_object : m_active_objects) { delete active_object.second; + // Object must be marked as gone when children try to detach + active_object.second = nullptr; } + m_active_objects.clear(); } void ActiveObjectMgr::step( float dtime, const std::function<void(ClientActiveObject *)> &f) { - g_profiler->avg("Client::ActiveObjectMgr: num of objects", - m_active_objects.size()); + g_profiler->avg("ActiveObjectMgr: CAO count [#]", m_active_objects.size()); for (auto &ao_it : m_active_objects) { f(ao_it.second); } @@ -91,15 +93,16 @@ void ActiveObjectMgr::removeObject(u16 id) void ActiveObjectMgr::getActiveObjects(const v3f &origin, f32 max_d, std::vector<DistanceSortedActiveObject> &dest) { + f32 max_d2 = max_d * max_d; for (auto &ao_it : m_active_objects) { ClientActiveObject *obj = ao_it.second; - f32 d = (obj->getPosition() - origin).getLength(); + f32 d2 = (obj->getPosition() - origin).getLengthSQ(); - if (d > max_d) + if (d2 > max_d2) continue; - dest.emplace_back(obj, d); + dest.emplace_back(obj, d2); } } diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 1bbdb56ea..d1e76026d 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -99,9 +99,9 @@ bool Camera::successfullyCreated(std::string &error_message) error_message.clear(); } - if (g_settings->getBool("enable_client_modding")) { + if (m_client->modsLoaded()) m_client->getScript()->on_camera_ready(this); - } + return error_message.empty(); } @@ -412,7 +412,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r // Prevent camera positioned inside nodes const NodeDefManager *nodemgr = m_client->ndef(); MapNode n = m_client->getEnv().getClientMap() - .getNodeNoEx(floatToInt(my_cp, BS)); + .getNode(floatToInt(my_cp, BS)); const ContentFeatures& features = nodemgr->get(n); if (features.walkable) { @@ -448,12 +448,26 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r if (m_camera_mode != CAMERA_MODE_FIRST) m_camera_position = my_cp; - // Get FOV + /* + * Apply server-sent FOV. If server doesn't enforce FOV, + * check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov + */ + f32 fov_degrees; - // Disable zoom with zoom FOV = 0 - if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { + PlayerFovSpec fov_spec = player->getFov(); + if (fov_spec.fov > 0.0f) { + // If server-sent FOV is a multiplier, multiply + // it with m_cache_fov instead of overriding + if (fov_spec.is_multiplier) + fov_degrees = m_cache_fov * fov_spec.fov; + else + fov_degrees = fov_spec.fov; + } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { + // Player requests zoom, apply zoom FOV fov_degrees = player->getZoomFOV(); } else { + // Set to client's selected FOV fov_degrees = m_cache_fov; } fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); diff --git a/src/client/client.cpp b/src/client/client.cpp index a4a379a73..caa3cc78c 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -108,30 +108,15 @@ Client::Client( m_minimap = new Minimap(this); } m_cache_save_interval = g_settings->getU16("server_map_save_interval"); - - m_modding_enabled = g_settings->getBool("enable_client_modding"); - // Only create the client script environment if client scripting is enabled by the - // client. - if (m_modding_enabled) { - m_script = new ClientScripting(this); - m_env.setScript(m_script); - m_script->setEnv(&m_env); - } } void Client::loadMods() { - // Don't load mods twice - if (m_mods_loaded) { - return; - } - + // Don't load mods twice. // If client scripting is disabled by the client, don't load builtin or // client-provided mods. - if (!m_modding_enabled) { - warningstream << "Client side scripting is disabled by client." << std::endl; + if (m_mods_loaded || !g_settings->getBool("enable_client_modding")) return; - } // If client scripting is disabled by the server, don't load builtin or // client-provided mods. @@ -140,11 +125,13 @@ void Client::loadMods() if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOAD_CLIENT_MODS)) { warningstream << "Client-provided mod loading is disabled by server." << std::endl; - // This line is needed because builtin is not loaded - m_modding_enabled = false; return; } + m_script = new ClientScripting(this); + m_env.setScript(m_script); + m_script->setEnv(&m_env); + // Load builtin scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); m_script->loadModFromMemory(BUILTIN_MOD_NAME); @@ -190,9 +177,15 @@ void Client::loadMods() for (const ModSpec &mod : m_mods) m_script->loadModFromMemory(mod.name); + // Mods are done loading. Unlock callbacks + m_mods_loaded = true; + // Run a callback when mods are loaded m_script->on_mods_loaded(); - m_mods_loaded = true; + if (m_state == LC_Ready) + m_script->on_client_ready(m_env.getLocalPlayer()); + if (m_camera) + m_script->on_camera_ready(m_camera); } bool Client::checkBuiltinIntegrity() @@ -244,7 +237,7 @@ const ModSpec* Client::getModSpec(const std::string &modname) const void Client::Stop() { m_shutdown = true; - if (m_modding_enabled) + if (m_mods_loaded) m_script->on_shutdown(); //request all client managed threads to stop m_mesh_update_thread.stop(); @@ -254,7 +247,7 @@ void Client::Stop() m_localdb->endSave(); } - if (m_modding_enabled) + if (m_mods_loaded) delete m_script; } @@ -371,7 +364,6 @@ void Client::step(float dtime) */ const float map_timer_and_unload_dtime = 5.25; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { - ScopeProfiler sp(g_profiler, "Client: map timer and unload"); std::vector<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), @@ -480,6 +472,7 @@ void Client::step(float dtime) */ { int num_processed_meshes = 0; + std::vector<v3s16> blocks_to_ack; while (!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; @@ -518,14 +511,18 @@ void Client::step(float dtime) m_minimap->addBlock(r.p, minimap_mapblock); if (r.ack_block_to_server) { - /* - Acknowledge block - [0] u8 count - [1] v3s16 pos_0 - */ - sendGotBlocks(r.p); + if (blocks_to_ack.size() == 255) { + sendGotBlocks(blocks_to_ack); + blocks_to_ack.clear(); + } + + blocks_to_ack.emplace_back(r.p); } } + if (blocks_to_ack.size() > 0) { + // Acknowledge block(s) + sendGotBlocks(blocks_to_ack); + } if (num_processed_meshes > 0) g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); @@ -558,8 +555,8 @@ void Client::step(float dtime) if (count_after != count_before) { // Do this every <interval> seconds after TOCLIENT_INVENTORY // Reset the locally changed inventory to the authoritative inventory - m_env.getLocalPlayer()->inventory = *m_inventory_from_server; - m_inventory_updated = true; + player->inventory = *m_inventory_from_server; + m_update_wielded_item = true; } } @@ -908,7 +905,7 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket * *pkt << fov << wanted_range; } -void Client::interact(u8 action, const PointedThing& pointed) +void Client::interact(InteractAction action, const PointedThing& pointed) { if(m_state != LC_Ready) { errorstream << "Client::interact() " @@ -928,19 +925,12 @@ void Client::interact(u8 action, const PointedThing& pointed) [5] u32 length of the next item (plen) [9] serialized PointedThing [9 + plen] player position information - actions: - 0: start digging (from undersurface) or use - 1: stop digging (all parameters ignored) - 2: digging completed - 3: place block or item (to abovesurface) - 4: use item - 5: perform secondary action of item */ NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0); - pkt << action; - pkt << (u16)getPlayerItem(); + pkt << (u8)action; + pkt << myplayer->getWieldIndex(); std::ostringstream tmp_os(std::ios::binary); pointed.serialize(tmp_os); @@ -1074,10 +1064,13 @@ void Client::sendDeletedBlocks(std::vector<v3s16> &blocks) Send(&pkt); } -void Client::sendGotBlocks(v3s16 block) +void Client::sendGotBlocks(const std::vector<v3s16> &blocks) { - NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6); - pkt << (u8) 1 << block; + NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6 * blocks.size()); + pkt << (u8) blocks.size(); + for (const v3s16 &block : blocks) + pkt << block; + Send(&pkt); } @@ -1200,7 +1193,7 @@ void Client::clearOutChatQueue() } void Client::sendChangePassword(const std::string &oldpassword, - const std::string &newpassword) + const std::string &newpassword) { LocalPlayer *player = m_env.getLocalPlayer(); if (player == NULL) @@ -1229,60 +1222,54 @@ void Client::sendRespawn() void Client::sendReady() { NetworkPacket pkt(TOSERVER_CLIENT_READY, - 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash)); + 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash) + 2); pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH << (u8) 0 << (u16) strlen(g_version_hash); pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash)); + pkt << (u16)FORMSPEC_API_VERSION; Send(&pkt); } void Client::sendPlayerPos() { - LocalPlayer *myplayer = m_env.getLocalPlayer(); - if (!myplayer) + LocalPlayer *player = m_env.getLocalPlayer(); + if (!player) return; ClientMap &map = m_env.getClientMap(); - - u8 camera_fov = map.getCameraFov(); - u8 wanted_range = map.getControl().wanted_range; - - // Save bandwidth by only updating position when something changed - if(myplayer->last_position == myplayer->getPosition() && - myplayer->last_speed == myplayer->getSpeed() && - myplayer->last_pitch == myplayer->getPitch() && - myplayer->last_yaw == myplayer->getYaw() && - myplayer->last_keyPressed == myplayer->keyPressed && - myplayer->last_camera_fov == camera_fov && - myplayer->last_wanted_range == wanted_range) + u8 camera_fov = map.getCameraFov(); + u8 wanted_range = map.getControl().wanted_range; + + // 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 ( + player->last_position == player->getPosition() && + player->last_speed == player->getSpeed() && + player->last_pitch == player->getPitch() && + player->last_yaw == player->getYaw() && + player->last_keyPressed == player->keyPressed && + player->last_camera_fov == camera_fov && + player->last_wanted_range == wanted_range) return; - myplayer->last_position = myplayer->getPosition(); - myplayer->last_speed = myplayer->getSpeed(); - myplayer->last_pitch = myplayer->getPitch(); - myplayer->last_yaw = myplayer->getYaw(); - myplayer->last_keyPressed = myplayer->keyPressed; - myplayer->last_camera_fov = camera_fov; - myplayer->last_wanted_range = wanted_range; + player->last_position = player->getPosition(); + player->last_speed = player->getSpeed(); + player->last_pitch = player->getPitch(); + player->last_yaw = player->getYaw(); + player->last_keyPressed = player->keyPressed; + player->last_camera_fov = camera_fov; + player->last_wanted_range = wanted_range; NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1); - writePlayerPos(myplayer, &map, &pkt); - - Send(&pkt); -} - -void Client::sendPlayerItem(u16 item) -{ - LocalPlayer *myplayer = m_env.getLocalPlayer(); - if (!myplayer) - return; - - NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); - - pkt << item; + writePlayerPos(player, &map, &pkt); Send(&pkt); } @@ -1318,7 +1305,7 @@ MapNode Client::getNode(v3s16 p, bool *is_valid_position) return {}; } } - return m_env.getMap().getNodeNoEx(p, is_valid_position); + return m_env.getMap().getNode(p, is_valid_position); } void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) @@ -1346,28 +1333,33 @@ void Client::setPlayerControl(PlayerControl &control) player->control = control; } -void Client::selectPlayerItem(u16 item) +void Client::setPlayerItem(u16 item) { - m_playeritem = item; - m_inventory_updated = true; - sendPlayerItem(item); -} + m_env.getLocalPlayer()->setWieldIndex(item); + m_update_wielded_item = true; -// Returns true if the inventory of the local player has been -// updated from the server. If it is true, it is set to false. -bool Client::getLocalInventoryUpdated() -{ - bool updated = m_inventory_updated; - m_inventory_updated = false; - return updated; + NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); + pkt << item; + Send(&pkt); } -// Copies the inventory of the local player to parameter -void Client::getLocalInventory(Inventory &dst) +// Returns true once after the inventory of the local player +// has been updated from the server. +bool Client::updateWieldedItem() { + if (!m_update_wielded_item) + return false; + + m_update_wielded_item = false; + LocalPlayer *player = m_env.getLocalPlayer(); assert(player); - dst = player->inventory; + if (auto *list = player->inventory.getList("main")) + list->setModified(false); + if (auto *list = player->inventory.getList("hand")) + list->setModified(false); + + return true; } Inventory* Client::getInventory(const InventoryLocation &loc) @@ -1510,7 +1502,7 @@ void Client::typeChatMessage(const std::wstring &message) return; // If message was consumed by script API, don't send it to server - if (m_modding_enabled && m_script->on_sending_message(wide_to_utf8(message))) + if (m_mods_loaded && m_script->on_sending_message(wide_to_utf8(message))) return; // Send to others @@ -1706,9 +1698,8 @@ void Client::afterContentReceived() m_state = LC_Ready; sendReady(); - if (g_settings->getBool("enable_client_modding")) { + if (m_mods_loaded) m_script->on_client_ready(m_env.getLocalPlayer()); - } text = wgettext("Done!"); RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 100); diff --git a/src/client/client.h b/src/client/client.h index 312b8c87f..e3c931837 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -193,6 +193,7 @@ public: void handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt); void handleCommand_ActiveObjectMessages(NetworkPacket* pkt); void handleCommand_Movement(NetworkPacket* pkt); + void handleCommand_Fov(NetworkPacket *pkt); void handleCommand_HP(NetworkPacket* pkt); void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_MovePlayer(NetworkPacket* pkt); @@ -227,12 +228,13 @@ public: void handleCommand_SrpBytesSandB(NetworkPacket *pkt); void handleCommand_FormspecPrepend(NetworkPacket *pkt); void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); + void handleCommand_PlayerSpeed(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); void Send(NetworkPacket* pkt); - void interact(u8 action, const PointedThing& pointed); + void interact(InteractAction action, const PointedThing &pointed); void sendNodemetaFields(v3s16 p, const std::string &formname, const StringMap &fields); @@ -271,20 +273,17 @@ public: void setPlayerControl(PlayerControl &control); - void selectPlayerItem(u16 item); - u16 getPlayerItem() const - { return m_playeritem; } - // Returns true if the inventory of the local player has been // updated from the server. If it is true, it is set to false. - bool getLocalInventoryUpdated(); - // Copies the inventory of the local player to parameter - void getLocalInventory(Inventory &dst); + bool updateWieldedItem(); /* InventoryManager interface */ Inventory* getInventory(const InventoryLocation &loc) override; void inventoryAction(InventoryAction *a) override; + // Send the item number 'item' as player item to the server + void setPlayerItem(u16 item); + const std::list<std::string> &getConnectedPlayerNames() { return m_env.getPlayerNames(); @@ -335,12 +334,14 @@ public: // disconnect client when CSM failed. const std::string &accessDeniedReason() const { return m_access_denied_reason; } - bool itemdefReceived() + const bool itemdefReceived() const { return m_itemdef_received; } - bool nodedefReceived() + const bool nodedefReceived() const { return m_nodedef_received; } - bool mediaReceived() + const bool mediaReceived() const { return !m_media_downloader; } + const bool activeObjectsReceived() const + { return m_activeobjects_received; } u16 getProtoVersion() { return m_proto_ver; } @@ -399,7 +400,6 @@ public: } ClientScripting *getScript() { return m_script; } - const bool moddingEnabled() const { return m_modding_enabled; } const bool modsLoaded() const { return m_mods_loaded; } void pushToEventQueue(ClientEvent *event); @@ -454,8 +454,6 @@ private: void Receive(); void sendPlayerPos(); - // Send the item number 'item' as player item to the server - void sendPlayerItem(u16 item); void deleteAuthData(); // helper method shared with clientpackethandler @@ -465,7 +463,7 @@ private: void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism); void startAuth(AuthMechanism chosen_auth_mechanism); void sendDeletedBlocks(std::vector<v3s16> &blocks); - void sendGotBlocks(v3s16 block); + void sendGotBlocks(const std::vector<v3s16> &blocks); void sendRemovedSounds(std::vector<s32> &soundList); // Helper function @@ -506,8 +504,7 @@ private: // If 0, server init hasn't been received yet. u16 m_proto_ver = 0; - u16 m_playeritem = 0; - bool m_inventory_updated = false; + bool m_update_wielded_item = false; Inventory *m_inventory_from_server = nullptr; float m_inventory_from_server_age = 0.0f; PacketCounter m_packetcounter; @@ -545,6 +542,7 @@ private: std::queue<ClientEvent *> m_client_event_queue; bool m_itemdef_received = false; bool m_nodedef_received = false; + bool m_activeobjects_received = false; bool m_mods_loaded = false; ClientMediaDownloader *m_media_downloader; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index a788c93c2..5eb033302 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -47,8 +47,6 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, m_texturesource(texturesource), m_client(client) { - char zero = 0; - memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); } ClientEnvironment::~ClientEnvironment() @@ -220,7 +218,7 @@ void ClientEnvironment::step(float dtime) f32 post_factor = 1; // 1 hp per node/s if (info.type == COLLISION_NODE) { const ContentFeatures &f = m_client->ndef()-> - get(m_map->getNodeNoEx(info.node_p)); + get(m_map->getNode(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); pre_factor = 1.0f + (float)addp / 100.0f; @@ -250,7 +248,7 @@ void ClientEnvironment::step(float dtime) MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); v3s16 p = lplayer->getLightPosition(); - node_at_lplayer = m_map->getNodeNoEx(p); + node_at_lplayer = m_map->getNode(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); final_color_blend(&lplayer->light_color, light, day_night_ratio); @@ -272,7 +270,7 @@ void ClientEnvironment::step(float dtime) // Get node at head v3s16 p = cao->getLightPosition(); - MapNode n = this->m_map->getNodeNoEx(p, &pos_ok); + MapNode n = this->m_map->getNode(p, &pos_ok); if (pos_ok) light = n.getLightBlend(day_night_ratio, m_client->ndef()); else @@ -287,15 +285,14 @@ void ClientEnvironment::step(float dtime) /* Step and handle simple objects */ - g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); + g_profiler->avg("ClientEnv: CSO count [#]", m_simple_objects.size()); for (auto i = m_simple_objects.begin(); i != m_simple_objects.end();) { - auto cur = i; - ClientSimpleObject *simple = *cur; + ClientSimpleObject *simple = *i; simple->step(dtime); if(simple->m_to_be_removed) { delete simple; - i = m_simple_objects.erase(cur); + i = m_simple_objects.erase(i); } else { ++i; @@ -353,7 +350,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) // Get node at head v3s16 p = object->getLightPosition(); - MapNode n = m_map->getNodeNoEx(p, &pos_ok); + MapNode n = m_map->getNode(p, &pos_ok); if (pos_ok) light = n.getLightBlend(getDayNightRatio(), m_client->ndef()); else @@ -392,7 +389,34 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, <<std::endl; } - addActiveObject(obj); + u16 new_id = addActiveObject(obj); + // Object initialized: + if ((obj = getActiveObject(new_id))) { + // Final step is to update all children which are already known + // Data provided by GENERIC_CMD_SPAWN_INFANT + const auto &children = obj->getAttachmentChildIds(); + for (auto c_id : children) { + if (auto *o = getActiveObject(c_id)) + o->updateAttachments(); + } + } +} + + +void ClientEnvironment::removeActiveObject(u16 id) +{ + // Get current attachment childs to detach them visually + std::unordered_set<int> attachment_childs; + if (auto *obj = getActiveObject(id)) + attachment_childs = obj->getAttachmentChildIds(); + + m_ao_manager.removeObject(id); + + // Perform a proper detach in Irrlicht + for (auto c_id : attachment_childs) { + if (ClientActiveObject *child = getActiveObject(c_id)) + child->updateAttachments(); + } } void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index 4fa3f4848..864496a41 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -104,10 +104,7 @@ public: u16 addActiveObject(ClientActiveObject *object); void addActiveObject(u16 id, u8 type, const std::string &init_data); - void removeActiveObject(u16 id) - { - m_ao_manager.removeObject(id); - } + void removeActiveObject(u16 id); void processActiveObjectMessage(u16 id, const std::string &data); @@ -138,8 +135,6 @@ public: std::vector<PointedThing> &objects ); - u16 attachement_parent_ids[USHRT_MAX + 1]; - const std::list<std::string> &getPlayerNames() { return m_player_names; } void addPlayerName(const std::string &name) { m_player_names.push_back(name); } void removePlayerName(const std::string &name) { m_player_names.remove(name); } diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 969c55539..3e4ab2e94 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -63,14 +63,13 @@ ClientMap::ClientMap( MapSector * ClientMap::emergeSector(v2s16 p2d) { // Check that it doesn't exist already - try { - return getSectorNoGenerate(p2d); - } catch(InvalidPositionException &e) { - } + MapSector *sector = getSectorNoGenerate(p2d); - // Create a sector - MapSector *sector = new MapSector(this, p2d, m_gamedef); - m_sectors[p2d] = sector; + // Create it if it does not exist yet + if (!sector) { + sector = new MapSector(this, p2d, m_gamedef); + m_sectors[p2d] = sector; + } return sector; } @@ -116,7 +115,6 @@ void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, void ClientMap::updateDrawList() { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); - g_profiler->add("CM::updateDrawList() count", 1); for (auto &i : m_drawlist) { MapBlock *block = i.second; @@ -138,34 +136,26 @@ void ClientMap::updateDrawList() v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); - // Number of blocks in rendering range - u32 blocks_in_range = 0; + // Number of blocks with mesh in rendering range + u32 blocks_in_range_with_mesh = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; - // Number of blocks in rendering range but don't have a mesh - u32 blocks_in_range_without_mesh = 0; - // Blocks that had mesh that would have been drawn according to - // rendering range (if max blocks limit didn't kick in) - u32 blocks_would_have_drawn = 0; - // Blocks that were drawn and had a mesh - u32 blocks_drawn = 0; - // Blocks which had a corresponding meshbuffer for this pass - //u32 blocks_had_pass_meshbuf = 0; - // Blocks from which stuff was actually drawn - //u32 blocks_without_stuff = 0; - // Distance to farthest drawn block - float farthest_drawn = 0; // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; - if (g_settings->getBool("free_move")) { - MapNode n = getNodeNoEx(cam_pos_nodes); + if (g_settings->getBool("free_move") && g_settings->getBool("noclip")) { + MapNode n = getNode(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2) occlusion_culling_enabled = false; } + // Uncomment to debug occluded blocks in the wireframe mode + // TODO: Include this as a flag for an extended debugging setting + //if (occlusion_culling_enabled && m_control.show_wireframe) + // occlusion_culling_enabled = porting::getTimeS() & 1; + for (const auto §or_it : m_sectors) { MapSector *sector = sector_it.second; v2s16 sp = sector->getPos(); @@ -185,11 +175,11 @@ void ClientMap::updateDrawList() u32 sector_blocks_drawn = 0; - for (auto block : sectorblocks) { + for (MapBlock *block : sectorblocks) { /* - Compare block position to camera position, skip - if not seen on display - */ + Compare block position to camera position, skip + if not seen on display + */ if (block->mesh) block->mesh->updateCameraOffset(m_camera_offset); @@ -203,20 +193,20 @@ void ClientMap::updateDrawList() camera_direction, camera_fov, range, &d)) continue; - blocks_in_range++; /* Ignore if mesh doesn't exist */ - if (!block->mesh) { - blocks_in_range_without_mesh++; + if (!block->mesh) continue; - } + + blocks_in_range_with_mesh++; /* Occlusion culling */ - if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) { + if ((!m_control.range_all && d > m_control.wanted_range * BS) || + (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) { blocks_occlusion_culled++; continue; } @@ -224,36 +214,20 @@ void ClientMap::updateDrawList() // This block is in range. Reset usage timer. block->resetUsageTimer(); - // Limit block count in case of a sudden increase - blocks_would_have_drawn++; - if (blocks_drawn >= m_control.wanted_max_blocks && - !m_control.range_all && - d > m_control.wanted_range * BS) - continue; - // Add to set block->refGrab(); m_drawlist[block->getPos()] = block; sector_blocks_drawn++; - blocks_drawn++; - if (d / BS > farthest_drawn) - farthest_drawn = d / BS; - } // foreach sectorblocks if (sector_blocks_drawn != 0) m_last_drawn_sectors.insert(sp); } - g_profiler->avg("CM: blocks in range", blocks_in_range); - g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); - if (blocks_in_range != 0) - g_profiler->avg("CM: blocks in range without mesh (frac)", - (float)blocks_in_range_without_mesh / blocks_in_range); - g_profiler->avg("CM: blocks drawn", blocks_drawn); - g_profiler->avg("CM: farthest drawn", farthest_drawn); - g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); + 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()); } struct MeshBufList @@ -306,9 +280,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) std::string prefix; if (pass == scene::ESNRP_SOLID) - prefix = "CM: solid: "; + prefix = "renderMap(SOLID): "; else - prefix = "CM: transparent: "; + prefix = "renderMap(TRANSPARENT): "; /* This is called two times per frame, reset on the non-transparent one @@ -317,14 +291,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m_last_drawn_sectors.clear(); /* - Get time for measuring timeout. - - Measuring time is very useful for long delays when the - machine is swapping a lot. - */ - std::time_t time1 = time(0); - - /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); @@ -340,26 +306,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) */ u32 vertex_count = 0; - u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; - u32 mesh_animate_count_far = 0; - - // Blocks that were drawn and had a mesh - u32 blocks_drawn = 0; - // Blocks which had a corresponding meshbuffer for this pass - u32 blocks_had_pass_meshbuf = 0; - // Blocks from which stuff was actually drawn - u32 blocks_without_stuff = 0; + //u32 mesh_animate_count_far = 0; /* Draw the selected MapBlocks */ - { - ScopeProfiler sp(g_profiler, prefix + "drawing blocks", SPT_AVG); - MeshBufListList drawbufs; for (auto &i : m_drawlist) { @@ -381,15 +336,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) assert(mapBlockMesh); // Pretty random but this should work somewhat nicely bool faraway = d >= BS * 50; - //bool faraway = d >= m_control.wanted_range * BS; if (mapBlockMesh->isAnimationForced() || !faraway || - mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { + mesh_animate_count < (m_control.range_all ? 200 : 50)) { + bool animated = mapBlockMesh->animate(faraway, animation_time, crack, daynight_ratio); if (animated) mesh_animate_count++; - if (animated && faraway) - mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } @@ -437,46 +390,33 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } + TimeTaker draw("Drawing mesh buffers"); + // Render all layers in order for (auto &lists : drawbufs.lists) { - int timecheck_counter = 0; for (MeshBufList &list : lists) { - timecheck_counter++; - if (timecheck_counter > 50) { - timecheck_counter = 0; - std::time_t time2 = time(0); - if (time2 > time1 + 4) { - infostream << "ClientMap::renderMap(): " - "Rendering takes ages, returning." - << std::endl; - return; - } + // Check and abort if the machine is swapping a lot + if (draw.getTimerTime() > 2000) { + infostream << "ClientMap::renderMap(): Rendering took >2s, " << + "returning." << std::endl; + return; } - driver->setMaterial(list.m); for (scene::IMeshBuffer *buf : list.bufs) { driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); - meshbuffer_count++; } } } - } // ScopeProfiler + g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true)); // Log only on solid pass because values are the same if (pass == scene::ESNRP_SOLID) { - g_profiler->avg("CM: animated meshes", mesh_animate_count); - g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); + g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count); } - g_profiler->avg(prefix + "vertices drawn", vertex_count); - if (blocks_had_pass_meshbuf != 0) - g_profiler->avg(prefix + "meshbuffers per block", - (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); - if (blocks_drawn != 0) - g_profiler->avg(prefix + "empty blocks (frac)", - (float)blocks_without_stuff / blocks_drawn); + g_profiler->avg(prefix + "vertices drawn [#]", vertex_count); } static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, @@ -497,7 +437,7 @@ static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, // Check content nearly at camera position { v3s16 p = floatToInt(p0 /*+ dir * 3*BS*/, BS); - MapNode n = map->getNodeNoEx(p); + MapNode n = map->getNode(p); if(ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates) allow_allowing_non_sunlight_propagates = true; @@ -505,7 +445,7 @@ static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, // If would start at CONTENT_IGNORE, start closer { v3s16 p = floatToInt(pf, BS); - MapNode n = map->getNodeNoEx(p); + MapNode n = map->getNode(p); if(n.getContent() == CONTENT_IGNORE){ float newd = 2*BS; pf = p0 + dir * 2*newd; @@ -519,7 +459,7 @@ static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, step *= step_multiplier; v3s16 p = floatToInt(pf, BS); - MapNode n = map->getNodeNoEx(p); + MapNode n = map->getNode(p); if (allow_allowing_non_sunlight_propagates && i == 0 && ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates) { @@ -555,6 +495,7 @@ static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int oldvalue, bool *sunlight_seen_result) { + ScopeProfiler sp(g_profiler, "CM::getBackgroundBrightness", SPT_AVG); static v3f z_directions[50] = { v3f(-100, 0, 0) }; @@ -621,7 +562,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int ret = 0; if(brightness_count == 0){ - MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS)); + MapNode n = getNode(floatToInt(m_camera_position, BS)); if(m_nodedef->get(n).param_type == CPT_LIGHT){ ret = decode_light(n.getLightBlend(daylight_factor, m_nodedef)); } else { @@ -640,7 +581,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) // Sadly ISceneManager has no "post effects" render pass, in that case we // could just register for that and handle it in renderMap(). - MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS)); + MapNode n = getNode(floatToInt(m_camera_position, BS)); // - If the player is in a solid node, make everything black. // - If the player is in liquid, draw a semi-transparent overlay. diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 8402bb00d..172e3a1d6 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -31,8 +31,6 @@ struct MapDrawControl bool range_all = false; // Wanted drawing range float wanted_range = 0.0f; - // Maximum number of blocks to draw - u32 wanted_max_blocks = 0; // show a wire frame for debugging bool show_wireframe = false; }; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index e3ad92dbc..6da99bbbf 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -254,6 +254,16 @@ void ClientMediaDownloader::initialStep(Client *client) fetch_request.post_data = required_hash_set; fetch_request.extra_headers.emplace_back( "Content-Type: application/octet-stream"); + + // Encapsulate possible IPv6 plain address in [] + std::string addr = client->getAddressName(); + if (addr.find(':', 0) != std::string::npos) + addr = '[' + addr + ']'; + fetch_request.extra_headers.emplace_back( + std::string("Referer: minetest://") + + addr + ":" + + std::to_string(client->getServerAddress().getPort())); + httpfetch_async(fetch_request); m_httpfetch_active++; diff --git a/src/client/clientobject.h b/src/client/clientobject.h index 9377d1e67..c673fff9a 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "activeobject.h" #include <unordered_map> +#include <unordered_set> + class ClientEnvironment; class ITextureSource; @@ -37,51 +39,53 @@ public: ClientActiveObject(u16 id, Client *client, ClientEnvironment *env); virtual ~ClientActiveObject(); - virtual void addToScene(ITextureSource *tsrc) {}; + virtual void addToScene(ITextureSource *tsrc) {} virtual void removeFromScene(bool permanent) {} // 0 <= light_at_pos <= LIGHT_SUN - virtual void updateLight(u8 light_at_pos){} - virtual void updateLightNoCheck(u8 light_at_pos){} - virtual v3s16 getLightPosition(){return v3s16(0,0,0);} + virtual void updateLight(u8 light_at_pos) {} + virtual void updateLightNoCheck(u8 light_at_pos) {} + virtual v3s16 getLightPosition() { return v3s16(0, 0, 0); } virtual bool getCollisionBox(aabb3f *toset) const { return false; } virtual bool getSelectionBox(aabb3f *toset) const { return false; } virtual bool collideWithObjects() const { return false; } - virtual v3f getPosition(){ return v3f(0,0,0); } - virtual float getYaw() const { return 0; } + virtual const v3f getPosition() const { return v3f(0.0f); } virtual scene::ISceneNode *getSceneNode() { return NULL; } virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() { return NULL; } - virtual bool isLocalPlayer() const {return false;} + virtual bool isLocalPlayer() const { return false; } + virtual ClientActiveObject *getParent() const { return nullptr; }; - virtual void setAttachments() {} - virtual bool doShowSelectionBox(){return true;} + virtual const std::unordered_set<int> &getAttachmentChildIds() const + { static std::unordered_set<int> rv; return rv; } + virtual void updateAttachments() {}; + + virtual bool doShowSelectionBox() { return true; } // Step object in time - virtual void step(float dtime, ClientEnvironment *env){} + virtual void step(float dtime, ClientEnvironment *env) {} // Process a message sent by the server side object - virtual void processMessage(const std::string &data){} + virtual void processMessage(const std::string &data) {} - virtual std::string infoText() {return "";} - virtual std::string debugInfoText() {return "";} + virtual std::string infoText() { return ""; } + virtual std::string debugInfoText() { return ""; } /* This takes the return value of ServerActiveObject::getClientInitializationData */ - virtual void initialize(const std::string &data){} + virtual void initialize(const std::string &data) {} // Create a certain type of ClientActiveObject - static ClientActiveObject* create(ActiveObjectType type, Client *client, - ClientEnvironment *env); + static ClientActiveObject *create(ActiveObjectType type, Client *client, + ClientEnvironment *env); // If returns true, punch will not be sent to the server - virtual bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL, - float time_from_last_punch=1000000) - { return false; } + virtual bool directReportPunch(v3f dir, const ItemStack *punchitem = nullptr, + float time_from_last_punch = 1000000) { return false; } protected: // Used for creating objects based on type - typedef ClientActiveObject* (*Factory)(Client *client, ClientEnvironment *env); + typedef ClientActiveObject *(*Factory)(Client *client, ClientEnvironment *env); static void registerType(u16 type, Factory f); Client *m_client; ClientEnvironment *m_env; @@ -90,10 +94,10 @@ private: static std::unordered_map<u16, Factory> m_types; }; -struct DistanceSortedActiveObject +class DistanceSortedActiveObject { +public: ClientActiveObject *obj; - f32 d; DistanceSortedActiveObject(ClientActiveObject *a_obj, f32 a_d) { @@ -105,4 +109,7 @@ struct DistanceSortedActiveObject { return d < other.d; } + +private: + f32 d; }; diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 13051f32c..887a62f25 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -99,7 +99,7 @@ void Clouds::render() //if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SOLID) return; - ScopeProfiler sp(g_profiler, "Rendering of clouds, avg", SPT_AVG); + ScopeProfiler sp(g_profiler, "Clouds::render()", SPT_AVG); int num_faces_to_draw = m_enable_3d ? 6 : 1; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 8643b5824..a15c1cc0b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -17,36 +17,35 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "content_cao.h" +#include <IBillboardSceneNode.h> #include <ICameraSceneNode.h> #include <ITextSceneNode.h> -#include <IBillboardSceneNode.h> #include <IMeshManipulator.h> #include <IAnimatedMeshSceneNode.h> -#include "content_cao.h" -#include "util/numeric.h" // For IntervalLimiter & setPitchYawRoll -#include "util/serialize.h" -#include "util/basic_macros.h" +#include "client/client.h" +#include "client/renderingengine.h" #include "client/sound.h" #include "client/tile.h" -#include "environment.h" +#include "util/basic_macros.h" +#include "util/numeric.h" // For IntervalLimiter & setPitchYawRoll +#include "util/serialize.h" +#include "camera.h" // CameraModes #include "collision.h" -#include "settings.h" -#include "serialization.h" // For decompressZlib -#include "clientobject.h" -#include "mesh.h" -#include "itemdef.h" -#include "tool.h" #include "content_cso.h" -#include "sound.h" -#include "nodedef.h" +#include "environment.h" +#include "itemdef.h" #include "localplayer.h" #include "map.h" -#include "camera.h" // CameraModes -#include "client.h" +#include "mesh.h" +#include "nodedef.h" +#include "serialization.h" // For decompressZlib +#include "settings.h" +#include "sound.h" +#include "tool.h" #include "wieldmesh.h" #include <algorithm> #include <cmath> -#include "client/renderingengine.h" class Settings; struct ToolCapabilities; @@ -305,6 +304,7 @@ void TestCAO::processMessage(const std::string &data) */ #include "genericobject.h" +#include "clientobject.h" GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): ClientActiveObject(0, client, env) @@ -372,6 +372,7 @@ void GenericCAO::processInitData(const std::string &data) m_position = readV3F32(is); m_rotation = readV3F32(is); m_hp = readU16(is); + const u8 num_messages = readU8(is); for (int i = 0; i < num_messages; i++) { @@ -400,7 +401,7 @@ bool GenericCAO::getSelectionBox(aabb3f *toset) const return true; } -v3f GenericCAO::getPosition() +const v3f GenericCAO::getPosition() const { if (getParent() != nullptr) { if (m_matrixnode) @@ -443,7 +444,7 @@ scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode() void GenericCAO::setChildrenVisible(bool toset) { - for (u16 cao_id : m_children) { + for (u16 cao_id : m_attachment_child_ids) { GenericCAO *obj = m_env->getGenericCAO(cao_id); if (obj) { obj->setVisible(toset); @@ -451,43 +452,81 @@ void GenericCAO::setChildrenVisible(bool toset) } } -void GenericCAO::setAttachments() +void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) { + 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; + + ClientActiveObject *parent = m_env->getActiveObject(parent_id); + + if (parent_id != old_parent) { + if (auto *o = m_env->getActiveObject(old_parent)) + o->removeAttachmentChild(m_id); + if (parent) + parent->addAttachmentChild(m_id); + } + updateAttachments(); } -ClientActiveObject* GenericCAO::getParent() const +void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const +{ + *parent_id = m_attachment_parent_id; + *bone = m_attachment_bone; + *position = m_attachment_position; + *rotation = m_attachment_rotation; +} + +void GenericCAO::clearChildAttachments() { - ClientActiveObject *obj = NULL; + // Cannot use for-loop here: setAttachment() modifies 'm_attachment_child_ids'! + while (!m_attachment_child_ids.empty()) { + int child_id = *m_attachment_child_ids.begin(); - u16 attached_id = m_env->attachement_parent_ids[getId()]; + if (ClientActiveObject *child = m_env->getActiveObject(child_id)) + child->setAttachment(0, "", v3f(), v3f()); - if ((attached_id != 0) && - (attached_id != getId())) { - obj = m_env->getActiveObject(attached_id); + removeAttachmentChild(child_id); } - return obj; } -void GenericCAO::removeFromScene(bool permanent) +void GenericCAO::clearParentAttachment() { - // Should be true when removing the object permanently and false when refreshing (eg: updating visuals) - if((m_env != NULL) && (permanent)) - { - for (u16 ci : m_children) { - if (m_env->attachement_parent_ids[ci] == getId()) { - m_env->attachement_parent_ids[ci] = 0; - } - } - m_children.clear(); + if (m_attachment_parent_id) + setAttachment(0, "", m_attachment_position, m_attachment_rotation); + else + setAttachment(0, "", v3f(), v3f()); +} - m_env->attachement_parent_ids[getId()] = 0; +void GenericCAO::addAttachmentChild(int child_id) +{ + m_attachment_child_ids.insert(child_id); +} - LocalPlayer* player = m_env->getLocalPlayer(); - if (this == player->parent) { - player->parent = nullptr; - player->isAttached = false; - } +void GenericCAO::removeAttachmentChild(int child_id) +{ + m_attachment_child_ids.erase(child_id); +} + +ClientActiveObject* GenericCAO::getParent() const +{ + return m_attachment_parent_id ? m_env->getActiveObject(m_attachment_parent_id) : + nullptr; +} + +void GenericCAO::removeFromScene(bool permanent) +{ + // Should be true when removing the object permanently + // and false when refreshing (eg: updating visuals) + if (m_env && permanent) { + // The client does not know whether this object does re-appear to + // a later time, thus do not clear child attachments. + + clearParentAttachment(); } if (m_meshnode) { @@ -711,6 +750,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) updateTextures(m_current_texture_modifier); scene::ISceneNode *node = getSceneNode(); + if (node && !m_prop.nametag.empty() && !m_is_local_player) { // Add nametag v3f pos; @@ -736,7 +776,7 @@ void GenericCAO::updateLight(u8 light_at_pos) updateLightNoCheck(light_at_pos); // Update light of all children - for (u16 i : m_children) { + for (u16 i : m_attachment_child_ids) { ClientActiveObject *obj = m_env->getActiveObject(i); if (obj) { obj->updateLightNoCheck(light_at_pos); @@ -871,12 +911,8 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Attachments, part 1: All attached objects must be unparented first, // or Irrlicht causes a segmentation fault - for (auto ci = m_children.begin(); ci != m_children.end();) { - if (m_env->attachement_parent_ids[*ci] != getId()) { - ci = m_children.erase(ci); - continue; - } - ClientActiveObject *obj = m_env->getActiveObject(*ci); + for (u16 cao_id : m_attachment_child_ids) { + ClientActiveObject *obj = m_env->getActiveObject(cao_id); if (obj) { scene::ISceneNode *child_node = obj->getSceneNode(); // The node's parent is always an IDummyTraformationSceneNode, @@ -884,18 +920,16 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) if (child_node) child_node->getParent()->setParent(m_smgr->getRootSceneNode()); } - ++ci; } removeFromScene(false); addToScene(m_client->tsrc()); // Attachments, part 2: Now that the parent has been refreshed, put its attachments back - for (u16 cao_id : m_children) { - // Get the object of the child + for (u16 cao_id : m_attachment_child_ids) { ClientActiveObject *obj = m_env->getActiveObject(cao_id); if (obj) - obj->setAttachments(); + obj->updateAttachments(); } } @@ -916,7 +950,6 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) { LocalPlayer *player = m_env->getLocalPlayer(); player->overridePosition = getParent()->getPosition(); - m_env->getLocalPlayer()->parent = getParent(); } } else { rot_translator.translate(dtime); @@ -960,7 +993,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) const NodeDefManager *ndef = m_client->ndef(); v3s16 p = floatToInt(getPosition() + v3f(0.0f, (m_prop.collisionbox.MinEdge.Y - 0.5f) * BS, 0.0f), BS); - MapNode n = m_env->getMap().getNodeNoEx(p); + MapNode n = m_env->getMap().getNode(p); SimpleSoundSpec spec = ndef->get(n).sound_footstep; // Reduce footstep gain, as non-local-player footsteps are // somehow louder. @@ -997,15 +1030,20 @@ 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)) { - float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset; - float max_rotation_delta = - dtime * m_prop.automatic_face_movement_max_rotation_per_sec; + float max_rotation_per_sec = + m_prop.automatic_face_movement_max_rotation_per_sec; - wrappedApproachShortest(m_rotation.Y, target_yaw, max_rotation_delta, 360.f); - rot_translator.val_current = m_rotation; + if (max_rotation_per_sec > 0) { + wrappedApproachShortest(m_rotation.Y, target_yaw, + dtime * max_rotation_per_sec, 360.f); + } else { + // Negative values of max_rotation_per_sec mean disabled. + m_rotation.Y = target_yaw; + } + rot_translator.val_current = m_rotation; updateNodePos(); } } @@ -1057,6 +1095,7 @@ void GenericCAO::updateTexturePos() } } +// Do not pass by reference, see header. void GenericCAO::updateTextures(std::string mod) { ITextureSource *tsrc = m_client->tsrc(); @@ -1225,18 +1264,19 @@ void GenericCAO::updateTextures(std::string mod) buf->getMaterial().AmbientColor = m_prop.colors[1]; buf->getMaterial().DiffuseColor = m_prop.colors[1]; buf->getMaterial().SpecularColor = m_prop.colors[1]; - setMeshColor(mesh, m_prop.colors[1]); } else if (!m_prop.colors.empty()) { buf->getMaterial().AmbientColor = m_prop.colors[0]; buf->getMaterial().DiffuseColor = m_prop.colors[0]; buf->getMaterial().SpecularColor = m_prop.colors[0]; - setMeshColor(mesh, m_prop.colors[0]); } buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter); buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter); } + // Set mesh color (only if lighting is disabled) + if (!m_prop.colors.empty() && m_glow < 0) + setMeshColor(mesh, m_prop.colors[0]); } } } @@ -1290,6 +1330,21 @@ void GenericCAO::updateBonePosition() void GenericCAO::updateAttachments() { ClientActiveObject *parent = getParent(); + + m_attached_to_local = parent && parent->isLocalPlayer(); + + /* + Following cases exist: + m_attachment_parent_id == 0 && !parent + This object is not attached + m_attachment_parent_id != 0 && parent + This object is attached + m_attachment_parent_id != 0 && !parent + This object will be attached as soon the parent is known + m_attachment_parent_id == 0 && parent + Impossible case + */ + if (!parent) { // Detach or don't attach if (m_matrixnode) { v3f old_pos = m_matrixnode->getAbsolutePosition(); @@ -1297,10 +1352,6 @@ void GenericCAO::updateAttachments() getPosRotMatrix().setTranslation(old_pos); m_matrixnode->updateAbsolutePosition(); } - if (m_is_local_player) { - LocalPlayer *player = m_env->getLocalPlayer(); - player->isAttached = false; - } } else // Attach { @@ -1319,10 +1370,11 @@ void GenericCAO::updateAttachments() getPosRotMatrix().setRotationDegrees(m_attachment_rotation); m_matrixnode->updateAbsolutePosition(); } - if (m_is_local_player) { - LocalPlayer *player = m_env->getLocalPlayer(); - player->isAttached = true; - } + } + if (m_is_local_player) { + LocalPlayer *player = m_env->getLocalPlayer(); + player->isAttached = parent; + player->parent = parent; } } @@ -1482,31 +1534,15 @@ void GenericCAO::processMessage(const std::string &data) updateBonePosition(); } else if (cmd == GENERIC_CMD_ATTACH_TO) { u16 parent_id = readS16(is); - u16 &old_parent_id = m_env->attachement_parent_ids[getId()]; - if (parent_id != old_parent_id) { - if (GenericCAO *old_parent = m_env->getGenericCAO(old_parent_id)) { - old_parent->m_children.erase(std::remove( - m_children.begin(), m_children.end(), - getId()), m_children.end()); - } - if (GenericCAO *new_parent = m_env->getGenericCAO(parent_id)) - new_parent->m_children.push_back(getId()); - - old_parent_id = parent_id; - } + std::string bone = deSerializeString(is); + v3f position = readV3F32(is); + v3f rotation = readV3F32(is); - m_attachment_bone = deSerializeString(is); - m_attachment_position = readV3F32(is); - m_attachment_rotation = readV3F32(is); + setAttachment(parent_id, bone, position, rotation); // localplayer itself can't be attached to localplayer - if (!m_is_local_player) { - m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer(); - // Objects attached to the local player should be hidden by default + if (!m_is_local_player) m_is_visible = !m_attached_to_local; - } - - updateAttachments(); } else if (cmd == GENERIC_CMD_PUNCHED) { u16 result_hp = readU16(is); @@ -1515,9 +1551,12 @@ void GenericCAO::processMessage(const std::string &data) m_hp = result_hp; + if (m_is_local_player) + m_env->getLocalPlayer()->hp = m_hp; + if (damage > 0) { - if (m_hp <= 0) + if (m_hp == 0) { // TODO: Execute defined fast response // As there is no definition, make a smoke puff @@ -1534,6 +1573,14 @@ void GenericCAO::processMessage(const std::string &data) updateTextures(m_current_texture_modifier + "^[brighten"); } } + + if (m_hp == 0) { + // Same as 'Server::DiePlayer' + clearParentAttachment(); + // Same as 'ObjectRef::l_remove' + if (!m_is_player) + clearChildAttachments(); + } } else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) { m_armor_groups.clear(); int armor_groups_size = readU16(is); @@ -1555,13 +1602,10 @@ void GenericCAO::processMessage(const std::string &data) } } else if (cmd == GENERIC_CMD_SPAWN_INFANT) { u16 child_id = readU16(is); - u8 type = readU8(is); + u8 type = readU8(is); // maybe this will be useful later + (void)type; - if (GenericCAO *childobj = m_env->getGenericCAO(child_id)) { - childobj->processInitData(deSerializeLongString(is)); - } else { - m_env->addActiveObject(child_id, type, deSerializeLongString(is)); - } + addAttachmentChild(child_id); } else { warningstream << FUNCTION_NAME << ": unknown command or outdated client \"" diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 3ce628d30..2c2d11077 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -102,10 +102,14 @@ private: bool m_animation_loop = true; // stores position and rotation for each bone name std::unordered_map<std::string, core::vector2d<v3f>> m_bone_position; + + int m_attachment_parent_id = 0; + std::unordered_set<int> m_attachment_child_ids; std::string m_attachment_bone = ""; v3f m_attachment_position; v3f m_attachment_rotation; bool m_attached_to_local = false; + int m_anim_frame = 0; int m_anim_num_frames = 1; float m_anim_framelength = 0.2f; @@ -122,8 +126,6 @@ private: bool m_is_visible = false; s8 m_glow = 0; - std::vector<u16> m_children; - public: GenericCAO(Client *client, ClientEnvironment *env); @@ -152,13 +154,15 @@ public: virtual bool getSelectionBox(aabb3f *toset) const; - v3f getPosition(); + const v3f getPosition() const; - inline const v3f &getRotation() + void setPosition(const v3f &pos) { - return m_rotation; + pos_translator.val_current = pos; } + inline const v3f &getRotation() const { return m_rotation; } + const bool isImmortal(); scene::ISceneNode *getSceneNode(); @@ -178,6 +182,12 @@ public: return m_matrixnode->getRelativeTransformationMatrix(); } + inline const core::matrix4 &getAbsolutePosRotMatrix() const + { + assert(m_matrixnode); + return m_matrixnode->getAbsoluteTransformation(); + } + inline f32 getStepHeight() const { return m_prop.stepheight; @@ -199,10 +209,17 @@ public: } void setChildrenVisible(bool toset); - + void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const; + void clearChildAttachments(); + void clearParentAttachment(); + void addAttachmentChild(int child_id); + void removeAttachmentChild(int child_id); ClientActiveObject *getParent() const; - - void setAttachments(); + const std::unordered_set<int> &getAttachmentChildIds() const + { return m_attachment_child_ids; } + void updateAttachments(); void removeFromScene(bool permanent); @@ -225,8 +242,8 @@ public: void updateTexturePos(); - // std::string copy is mandatory as mod can be a class member and there is a swap - // on those class members... do NOT pass by reference + // ffs this HAS TO BE a string copy! See #5739 if you think otherwise + // Reason: updateTextures(m_previous_texture_modifier); void updateTextures(std::string mod); void updateAnimation(); @@ -235,8 +252,6 @@ public: void updateBonePosition(); - void updateAttachments(); - void processMessage(const std::string &data); bool directReportPunch(v3f dir, const ItemStack *punchitem=NULL, diff --git a/src/client/content_cso.cpp b/src/client/content_cso.cpp index 04c503f44..f9641afbe 100644 --- a/src/client/content_cso.cpp +++ b/src/client/content_cso.cpp @@ -48,7 +48,7 @@ public: /* Update brightness */ u8 light; bool pos_ok; - MapNode n = env->getMap().getNodeNoEx(floatToInt(pos, BS), &pos_ok); + MapNode n = env->getMap().getNode(floatToInt(pos, BS), &pos_ok); light = pos_ok ? decode_light(n.getLightBlend(env->getDayNightRatio(), env->getGameDef()->ndef())) : 64; diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 59e5bedee..858d6780e 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -23,9 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #include "porting.h" #include "filesys.h" +#include "gettext.h" #if USE_FREETYPE -#include "gettext.h" #include "irrlicht_changes/CGUITTFont.h" #endif @@ -55,36 +55,7 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) : assert(m_env != NULL); // pre-condition assert(m_env->getSkin() != NULL); // pre-condition - m_currentMode = FM_Simple; - -#if USE_FREETYPE - if (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"); - - if (is_yes(gettext("needs_fallback_font"))) { - m_currentMode = FM_Fallback; - } - else { - m_currentMode = FM_Standard; - } - } - - // having freetype but not using it is quite a strange case so we need to do - // special handling for it - if (m_currentMode == FM_Simple) { - std::stringstream fontsize; - fontsize << DEFAULT_FONT_SIZE; - m_settings->setDefault("font_size", fontsize.str()); - m_settings->setDefault("mono_font_size", fontsize.str()); - } -#endif - - m_default_size[FM_Simple] = m_settings->getU16("font_size"); - m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size"); - - updateSkin(); + readSettings(); if (m_currentMode == FM_Standard) { m_settings->registerChangedCallback("font_size", font_setting_changed, NULL); @@ -129,32 +100,26 @@ irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode) { if (mode == FM_Unspecified) { mode = m_currentMode; - } - else if ((mode == FM_Mono) && (m_currentMode == FM_Simple)) { - mode = FM_SimpleMono; + } else if (m_currentMode == FM_Simple) { + // Freetype disabled -> Force simple mode + mode = (mode == FM_Mono || mode == FM_SimpleMono) ? + FM_SimpleMono : FM_Simple; } - if (font_size == FONT_SIZE_UNSPECIFIED) { + // Fallback to default size + if (font_size == FONT_SIZE_UNSPECIFIED) font_size = m_default_size[mode]; - } - - if ((font_size == m_lastSize) && (mode == m_lastMode)) { - return m_lastFont; - } - - if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) { - initFont(font_size, mode); - } - if (m_font_cache[mode].find(font_size) == m_font_cache[mode].end()) { - return NULL; + const auto &cache = m_font_cache[mode]; + if (cache.find(font_size) == cache.end()) { + if (mode == FM_Simple || mode == FM_SimpleMono) + initSimpleFont(font_size, mode); + else + initFont(font_size, mode); } - m_lastSize = font_size; - m_lastMode = mode; - m_lastFont = m_font_cache[mode][font_size]; - - return m_font_cache[mode][font_size]; + const auto &font = cache.find(font_size); + return font != cache.end() ? font->second : nullptr; } /******************************************************************************/ @@ -211,20 +176,17 @@ unsigned int FontEngine::getDefaultFontSize() /******************************************************************************/ void FontEngine::readSettings() { -#if USE_FREETYPE - if (g_settings->getBool("freetype")) { + 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"); - if (is_yes(gettext("needs_fallback_font"))) { - m_currentMode = FM_Fallback; - } - else { - m_currentMode = FM_Standard; - } + m_currentMode = is_yes(gettext("needs_fallback_font")) ? + FM_Fallback : FM_Standard; + } else { + m_currentMode = FM_Simple; } -#endif + m_default_size[FM_Simple] = m_settings->getU16("font_size"); m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size"); @@ -260,81 +222,55 @@ void FontEngine::updateFontCache() { /* the only font to be initialized is default one, * all others are re-initialized on demand */ - initFont(m_default_size[m_currentMode], m_currentMode); - - /* reset font quick access */ - m_lastMode = FM_Unspecified; - m_lastSize = 0; - m_lastFont = NULL; + getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified); } /******************************************************************************/ void FontEngine::initFont(unsigned int basesize, FontMode mode) { + assert(mode != FM_Unspecified); + assert(basesize != FONT_SIZE_UNSPECIFIED); - std::string font_config_prefix; - - if (mode == FM_Unspecified) { - mode = m_currentMode; - } + if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end()) + return; - switch (mode) { - case FM_Standard: - font_config_prefix = ""; - break; + std::string setting_prefix = ""; + switch (mode) { case FM_Fallback: - font_config_prefix = "fallback_"; + setting_prefix = "fallback_"; break; - case FM_Mono: - font_config_prefix = "mono_"; - if (m_currentMode == FM_Simple) - mode = FM_SimpleMono; + case FM_SimpleMono: + setting_prefix = "mono_"; break; - - case FM_Simple: /* Fallthrough */ - case FM_SimpleMono: /* Fallthrough */ default: - font_config_prefix = ""; - + break; } - if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end()) - return; - - if ((mode == FM_Simple) || (mode == FM_SimpleMono)) { - initSimpleFont(basesize, mode); - return; + u32 size = std::floor(RenderingEngine::getDisplayDensity() * + m_settings->getFloat("gui_scaling") * basesize); + if (size == 0) { + errorstream << "FontEngine: attempt to use font size 0" << std::endl; + errorstream << " display density: " << RenderingEngine::getDisplayDensity() << std::endl; + abort(); } -#if USE_FREETYPE - else { - if (!is_yes(m_settings->get("freetype"))) { - return; - } - u32 size = std::floor(RenderingEngine::getDisplayDensity() * - m_settings->getFloat("gui_scaling") * basesize); - if (size == 0) { - errorstream << "FontEngine: attempt to use font size 0" << std::endl; - errorstream << " display density: " << RenderingEngine::getDisplayDensity() << std::endl; - abort(); - } - u32 font_shadow = 0; - u32 font_shadow_alpha = 0; - try { - font_shadow = - g_settings->getU16(font_config_prefix + "font_shadow"); - } catch (SettingNotFoundException&) {} - try { - font_shadow_alpha = - g_settings->getU16(font_config_prefix + "font_shadow_alpha"); - } catch (SettingNotFoundException&) {} + u16 font_shadow = 0; + u16 font_shadow_alpha = 0; + g_settings->getU16NoEx(setting_prefix + "font_shadow", font_shadow); + g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha", font_shadow_alpha); - std::string font_path = g_settings->get(font_config_prefix + "font_path"); + std::string fallback_settings[] = { + m_settings->get(setting_prefix + "font_path"), + m_settings->get("fallback_font_path"), + m_settings->getDefault(setting_prefix + "font_path") + }; - irr::gui::IGUIFont* font = gui::CGUITTFont::createTTFont(m_env, +#if USE_FREETYPE + for (const std::string &font_path : fallback_settings) { + irr::gui::IGUIFont *font = gui::CGUITTFont::createTTFont(m_env, font_path.c_str(), size, true, true, font_shadow, font_shadow_alpha); @@ -343,93 +279,42 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) return; } - if (font_config_prefix == "mono_") { - const std::string &mono_font_path = m_settings->getDefault("mono_font_path"); - - if (font_path != mono_font_path) { - // try original mono font - errorstream << "FontEngine: failed to load custom mono " - "font: " << font_path << ", trying to fall back to " - "original mono font" << std::endl; - - font = gui::CGUITTFont::createTTFont(m_env, - mono_font_path.c_str(), size, true, true, - font_shadow, font_shadow_alpha); - - if (font) { - m_font_cache[mode][basesize] = font; - return; - } - } - } else { - // try fallback font - errorstream << "FontEngine: failed to load: " << font_path << - ", trying to fall back to fallback font" << std::endl; - - font_path = g_settings->get(font_config_prefix + "fallback_font_path"); - - font = gui::CGUITTFont::createTTFont(m_env, - font_path.c_str(), size, true, true, font_shadow, - font_shadow_alpha); - - if (font) { - m_font_cache[mode][basesize] = font; - return; - } - - const std::string &fallback_font_path = m_settings->getDefault("fallback_font_path"); - - if (font_path != fallback_font_path) { - // try original fallback font - errorstream << "FontEngine: failed to load custom fallback " - "font: " << font_path << ", trying to fall back to " - "original fallback font" << std::endl; - - font = gui::CGUITTFont::createTTFont(m_env, - fallback_font_path.c_str(), size, true, true, - font_shadow, font_shadow_alpha); + errorstream << "FontEngine: Cannot load '" << font_path << + "'. Trying to fall back to another path." << std::endl; + } - if (font) { - m_font_cache[mode][basesize] = font; - return; - } - } - } - // give up - errorstream << "FontEngine: failed to load freetype font: " - << font_path << std::endl; - errorstream << "minetest can not continue without a valid font. " - "Please correct the 'font_path' setting or install the font " - "file in the proper location" << std::endl; - abort(); - } + // give up + errorstream << "minetest can not continue without a valid font. " + "Please correct the 'font_path' setting or install the font " + "file in the proper location" << std::endl; +#else + errorstream << "FontEngine: Tried to load freetype fonts but Minetest was" + " not compiled with that library." << std::endl; #endif + abort(); } /** initialize a font without freetype */ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) { - assert(mode == FM_Simple || mode == FM_SimpleMono); // pre-condition + assert(mode == FM_Simple || mode == FM_SimpleMono); - std::string font_path; - if (mode == FM_Simple) { - font_path = m_settings->get("font_path"); - } else { - font_path = m_settings->get("mono_font_path"); - } + const std::string &font_path = m_settings->get( + (mode == FM_SimpleMono) ? "mono_font_path" : "font_path"); + + size_t pos_dot = font_path.find_last_of('.'); std::string basename = font_path; - std::string ending = font_path.substr(font_path.length() -4); + std::string ending = lowercase(font_path.substr(pos_dot)); if (ending == ".ttf") { - errorstream << "FontEngine: Not trying to open \"" << font_path - << "\" which seems to be a truetype font." << std::endl; + errorstream << "FontEngine: Found font \"" << font_path + << "\" but freetype is not available." << std::endl; return; } - if ((ending == ".xml") || (ending == ".png")) { - basename = font_path.substr(0,font_path.length()-4); - } + if (ending == ".xml" || ending == ".png") + basename = font_path.substr(0, pos_dot); if (basesize == FONT_SIZE_UNSPECIFIED) basesize = DEFAULT_FONT_SIZE; @@ -439,59 +324,35 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) m_settings->getFloat("gui_scaling") * basesize); - irr::gui::IGUIFont* font = NULL; + irr::gui::IGUIFont *font = nullptr; + std::string font_extensions[] = { ".png", ".xml" }; - for(unsigned int offset = 0; offset < MAX_FONT_SIZE_OFFSET; offset++) { + // Find nearest matching font scale + // Does a "zig-zag motion" (positibe/negative), from 0 to MAX_FONT_SIZE_OFFSET + for (s32 zoffset = 0; zoffset < MAX_FONT_SIZE_OFFSET * 2; zoffset++) { + std::stringstream path; - // try opening positive offset - std::stringstream fontsize_plus_png; - fontsize_plus_png << basename << "_" << (size + offset) << ".png"; + // LSB to sign + s32 sign = (zoffset & 1) ? -1 : 1; + s32 offset = zoffset >> 1; - if (fs::PathExists(fontsize_plus_png.str())) { - font = m_env->getFont(fontsize_plus_png.str().c_str()); + for (const std::string &ext : font_extensions) { + path.str(""); // Clear + path << basename << "_" << (size + offset * sign) << ext; - if (font) { - verbosestream << "FontEngine: found font: " << fontsize_plus_png.str() << std::endl; - break; - } - } - - std::stringstream fontsize_plus_xml; - fontsize_plus_xml << basename << "_" << (size + offset) << ".xml"; + if (!fs::PathExists(path.str())) + continue; - if (fs::PathExists(fontsize_plus_xml.str())) { - font = m_env->getFont(fontsize_plus_xml.str().c_str()); + font = m_env->getFont(path.str().c_str()); if (font) { - verbosestream << "FontEngine: found font: " << fontsize_plus_xml.str() << std::endl; + verbosestream << "FontEngine: found font: " << path.str() << std::endl; break; } } - // try negative offset - std::stringstream fontsize_minus_png; - fontsize_minus_png << basename << "_" << (size - offset) << ".png"; - - if (fs::PathExists(fontsize_minus_png.str())) { - font = m_env->getFont(fontsize_minus_png.str().c_str()); - - if (font) { - verbosestream << "FontEngine: found font: " << fontsize_minus_png.str() << std::endl; - break; - } - } - - std::stringstream fontsize_minus_xml; - fontsize_minus_xml << basename << "_" << (size - offset) << ".xml"; - - if (fs::PathExists(fontsize_minus_xml.str())) { - font = m_env->getFont(fontsize_minus_xml.str().c_str()); - - if (font) { - verbosestream << "FontEngine: found font: " << fontsize_minus_xml.str() << std::endl; - break; - } - } + if (font) + break; } // try name direct @@ -503,8 +364,6 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) } } - if (font) { - font->grab(); + if (font) m_font_cache[mode][basesize] = font; - } } diff --git a/src/client/fontengine.h b/src/client/fontengine.h index a75618f86..62aa71897 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -112,15 +112,6 @@ private: /** current font engine mode */ FontMode m_currentMode = FM_Standard; - /** font mode of last request */ - FontMode m_lastMode; - - /** size of last request */ - unsigned int m_lastSize = 0; - - /** last font returned */ - irr::gui::IGUIFont* m_lastFont = nullptr; - DISABLE_CLASS_COPY(FontEngine); }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 37680dda3..450eb4e32 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -55,7 +55,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include "porting.h" #include "profiler.h" -#include "quicktune_shortcutter.h" #include "raycast.h" #include "server.h" #include "settings.h" @@ -65,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/basic_macros.h" #include "util/directiontables.h" #include "util/pointedthing.h" +#include "util/quicktune_shortcutter.h" #include "irrlicht_changes/static_text.h" #include "version.h" #include "script/scripting_client.h" @@ -184,7 +184,7 @@ struct LocalFormspecHandler : public TextDest return; } - if (m_client && m_client->moddingEnabled()) + if (m_client && m_client->modsLoaded()) m_client->getScript()->on_formspec_input(m_formname, fields); } @@ -413,6 +413,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter 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; @@ -445,6 +447,8 @@ public: 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"), @@ -493,7 +497,7 @@ public: sunlight.b }; m_day_light.set(dnc, services); - u32 animation_timer = porting::getTimeMs() % 100000; + u32 animation_timer = porting::getTimeMs() % 1000000; float animation_timer_f = (float)animation_timer / 100000.f; m_animation_timer_vertex.set(&animation_timer_f, services); m_animation_timer_pixel.set(&animation_timer_f, services); @@ -523,6 +527,18 @@ public: m_minimap_yaw.set(minimap_yaw_array, services); } + float camera_offset_array[3]; + v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + camera_offset_array[0] = offset.X; + camera_offset_array[1] = offset.Y; + camera_offset_array[2] = offset.Z; +#else + offset.getAs3Values(camera_offset_array); +#endif + 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; @@ -599,7 +615,6 @@ struct GameRunData { bool dig_instantly; bool digging_blocked; bool left_punch; - bool update_wielded_item_trigger; bool reset_jump_timer; float nodig_delay_timer; float dig_time; @@ -689,8 +704,8 @@ protected: bool handleCallbacks(); void processQueues(); void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime); - void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times, f32 dtime); void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); + void updateProfilerGraphs(ProfilerGraph *graph); // Input related void processUserInput(f32 dtime); @@ -744,15 +759,13 @@ protected: bool look_for_object, const v3s16 &camera_offset); void handlePointingAtNothing(const ItemStack &playerItem); void handlePointingAtNode(const PointedThing &pointed, - const ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, f32 dtime); + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, const v3f &player_position, bool show_debug); void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ToolCapabilities &playeritem_toolcap, f32 dtime); + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam); - void updateProfilerGraphs(ProfilerGraph *graph); // Misc void limitFps(FpsControl *fps_timings, f32 *dtime); @@ -804,8 +817,9 @@ private: void updateChat(f32 dtime, const v2u32 &screensize); - bool nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos); + bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, + const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, + const NodeMetadata *meta); static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; InputHandler *input = nullptr; @@ -827,10 +841,6 @@ private: ChatBackend *chat_backend = nullptr; - GUIFormSpecMenu *current_formspec = nullptr; - //default: "". If other than "", empty show_formspec packets will only close the formspec when the formname matches - std::string cur_formname; - EventManager *eventmgr = nullptr; QuicktuneShortcutter *quicktune = nullptr; bool registration_confirmation_shown = false; @@ -841,7 +851,6 @@ private: Camera *camera = nullptr; Clouds *clouds = nullptr; // Free using ->Drop() Sky *sky = nullptr; // Free using ->Drop() - Inventory *local_inventory = nullptr; Hud *hud = nullptr; Minimap *mapper = nullptr; @@ -955,7 +964,6 @@ Game::~Game() delete server; // deleted first to stop all server threads delete hud; - delete local_inventory; delete camera; delete quicktune; delete eventmgr; @@ -1026,7 +1034,6 @@ bool Game::startup(bool *kill, // Reinit runData runData = GameRunData(); runData.time_from_last_punch = 10.0; - runData.update_wielded_item_trigger = true; m_game_ui->initFlags(); @@ -1089,11 +1096,13 @@ void Game::run() previous_screen_size = current_screen_size; } - /* Must be called immediately after a device->run() call because it - * uses device->getTimer()->getTime() - */ + // Calculate dtime = + // RenderingEngine::run() from this iteration + // + Sleep time until the wanted FPS are reached limitFps(&draw_times, &dtime); + // Prepare render data for next iteration + updateStats(&stats, draw_times, dtime); updateInteractTimers(dtime); @@ -1143,8 +1152,9 @@ void Game::shutdown() driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); } #endif - if (current_formspec) - current_formspec->quitMenu(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec) + formspec->quitMenu(); showOverlayMessage(N_("Shutting down..."), 0, 0, false); @@ -1163,10 +1173,7 @@ void Game::shutdown() g_menumgr.deletingMenu(g_menumgr.m_stack.front()); } - if (current_formspec) { - current_formspec->drop(); - current_formspec = NULL; - } + m_game_ui->deleteFormspec(); chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); @@ -1355,10 +1362,8 @@ bool Game::createClient(const std::string &playername, scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop - local_inventory = new Inventory(itemdef_manager); - - if (!(sky && local_inventory)) { - *error_message = "Memory allocation error (sky or local inventory)"; + if (!sky) { + *error_message = "Memory allocation error sky"; errorstream << *error_message << std::endl; return false; } @@ -1390,7 +1395,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(guienv, client, player, local_inventory); + hud = new Hud(guienv, client, player, &player->inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -1545,7 +1550,7 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, *address, connection_aborted))->drop(); + &g_menumgr, client, playername, password, connection_aborted))->drop(); } } else { wait_time += dtime; @@ -1733,7 +1738,8 @@ void Game::processQueues() } -void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime) +void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, + f32 dtime) { float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); @@ -1741,7 +1747,7 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, if (profiler_print_interval == 0) { print_to_log = false; - profiler_print_interval = 5; + profiler_print_interval = 3; } if (profiler_interval.step(dtime, profiler_print_interval)) { @@ -1754,25 +1760,14 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, g_profiler->clear(); } - addProfilerGraphs(stats, draw_times, dtime); -} - - -void Game::addProfilerGraphs(const RunStats &stats, - const FpsControl &draw_times, f32 dtime) -{ - g_profiler->graphAdd("mainloop_other", - draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f); - - if (draw_times.sleep_time != 0) - g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f); - g_profiler->graphAdd("mainloop_dtime", dtime); + // Update update graphs + g_profiler->graphAdd("Time non-rendering [ms]", + draw_times.busy_time - stats.drawtime); - g_profiler->add("Elapsed time", dtime); - g_profiler->avg("FPS", 1. / dtime); + g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time); + g_profiler->graphAdd("FPS", 1.0f / dtime); } - void Game::updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime) { @@ -1853,8 +1848,9 @@ void Game::processUserInput(f32 dtime) input->step(dtime); #ifdef __ANDROID__ - if (current_formspec != NULL) - current_formspec->getAndroidUIInput(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec) + formspec->getAndroidUIInput(); else handleAndroidChatInput(); #endif @@ -1880,6 +1876,9 @@ void Game::processKeyInput() } else if (wasKeyDown(KeyType::INVENTORY)) { openInventory(); } else if (input->cancelPressed()) { +#ifdef __ANDROID__ + m_android_chat_open = false; +#endif if (!gui_chat_console->isOpenInhibited()) { showPauseMenu(); } @@ -1888,7 +1887,7 @@ void Game::processKeyInput() } else if (wasKeyDown(KeyType::CMD)) { openConsole(0.2, L"/"); } else if (wasKeyDown(KeyType::CMD_LOCAL)) { - if (client->moddingEnabled()) + if (client->modsLoaded()) openConsole(0.2, L"."); else m_game_ui->showStatusText(wgettext("Client side scripting is disabled")); @@ -1979,7 +1978,7 @@ void Game::processItemSelection(u16 *new_playeritem) /* Item selection using mouse wheel */ - *new_playeritem = client->getPlayerItem(); + *new_playeritem = player->getWieldIndex(); s32 wheel = input->getMouseWheel(); u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1, @@ -2021,7 +2020,7 @@ void Game::dropSelectedItem(bool single_item) a->count = single_item ? 1 : 0; a->from_inv.setCurrentPlayer(); a->from_list = "main"; - a->from_i = client->getPlayerItem(); + a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex(); client->inventoryAction(a); } @@ -2044,13 +2043,14 @@ void Game::openInventory() InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); - if (!client->moddingEnabled() + if (!client->modsLoaded() || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { TextDest *txt_dst = new TextDestPlayerInventory(client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, + auto *&formspec = m_game_ui->updateFormspec(""); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - cur_formname = ""; - current_formspec->setFormSpec(fs_src->getForm(), inventoryloc); + + formspec->setFormSpec(fs_src->getForm(), inventoryloc); } } @@ -2079,6 +2079,7 @@ void Game::handleAndroidChatInput() if (m_android_chat_open && porting::getInputDialogState() == 0) { std::string text = porting::getInputDialogValue(); client->typeChatMessage(utf8_to_wide(text)); + m_android_chat_open = false; } } #endif @@ -2348,7 +2349,7 @@ void Game::toggleFullViewRange() void Game::checkZoomEnabled() { LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (player->getZoomFOV() < 0.001f) + if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f) m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); } @@ -2479,6 +2480,13 @@ void Game::updatePlayerControl(const CameraOrientation &cam) keypress_bits |= 1U << 4; } + // autoforward if set: simulate "up" key + if (player->getPlayerSettings().continuous_forward && + client->activeObjectsReceived() && !player->isDead()) { + control.up = true; + keypress_bits |= 1U << 0; + } + client->setPlayerControl(control); player->keyPressed = keypress_bits; @@ -2491,7 +2499,7 @@ inline void Game::step(f32 *dtime) bool can_be_and_is_paused = (simple_singleplayer_mode && g_menumgr.pausesGame()); - if (can_be_and_is_paused) { // This is for a singleplayer server + if (can_be_and_is_paused) { // This is for a singleplayer server *dtime = 0; // No time passes } else { if (server) @@ -2526,9 +2534,8 @@ void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam) { - if (client->moddingEnabled()) { + if (client->modsLoaded()) client->getScript()->on_damage_taken(event->player_damage.amount); - } // Damage flash and hurt tilt are not used at death if (client->getHP() > 0) { @@ -2556,7 +2563,7 @@ void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation * { // If client scripting is enabled, deathscreen is handled by CSM code in // builtin/client/init.lua - if (client->moddingEnabled()) + if (client->modsLoaded()) client->getScript()->on_death(); else showDeathFormspec(); @@ -2571,9 +2578,10 @@ void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation * void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam) { if (event->show_formspec.formspec->empty()) { - if (current_formspec && (event->show_formspec.formname->empty() - || *(event->show_formspec.formname) == cur_formname)) { - current_formspec->quitMenu(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec && (event->show_formspec.formname->empty() + || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) { + formspec->quitMenu(); } } else { FormspecFormSource *fs_src = @@ -2581,9 +2589,9 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation TextDestPlayerInventory *txt_dst = new TextDestPlayerInventory(client, *(event->show_formspec.formname)); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - cur_formname = *(event->show_formspec.formname); } delete event->show_formspec.formspec; @@ -2595,7 +2603,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); delete event->show_formspec.formspec; @@ -2840,19 +2848,10 @@ void Game::updateCamera(u32 busy_time, f32 dtime) */ ItemStack playeritem; { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - if (playeritem.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - playeritem = hlist->getItem(0); + ItemStack selected, hand; + playeritem = player->getWieldedItem(&selected, &hand); } - ToolCapabilities playeritem_toolcap = playeritem.getToolCapabilities(itemdef_manager); @@ -2933,7 +2932,7 @@ void Game::updateSound(f32 dtime) soundmaker->step(dtime); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = map.getNodeNoEx(player->getFootstepNodePos()); + MapNode n = map.getNode(player->getFootstepNodePos()); soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; } @@ -2942,46 +2941,35 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - ItemStack playeritem; - { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - const ItemDefinition &playeritem_def = - playeritem.getDefinition(itemdef_manager); - InventoryList *hlist = local_inventory->getList("hand"); - const ItemDefinition &hand_def = - hlist ? hlist->getItem(0).getDefinition(itemdef_manager) : itemdef_manager->get(""); - v3f player_position = player->getPosition(); + v3f player_eye_position = player->getEyePosition(); v3f camera_position = camera->getPosition(); v3f camera_direction = camera->getDirection(); v3s16 camera_offset = camera->getOffset(); + if (camera->getCameraMode() == CAMERA_MODE_FIRST) + player_eye_position += player->eye_offset_first; + else + player_eye_position += player->eye_offset_third; /* Calculate what block is the crosshair pointing to */ - f32 d = playeritem_def.range; // max. distance - f32 d_hand = hand_def.range; + ItemStack selected_item, hand_item; + const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); - if (d < 0 && d_hand >= 0) - d = d_hand; - else if (d < 0) - d = 4.0; + const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); + f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager)); core::line3d<f32> shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d<f32>(camera_position, - camera_position + camera_direction * BS * d); + shootline = core::line3d<f32>(player_eye_position, + player_eye_position + camera_direction * BS * d); } else { - // prevent player pointing anything in front-view - shootline = core::line3d<f32>(camera_position,camera_position); + // prevent player pointing anything in front-view + shootline = core::line3d<f32>(camera_position, camera_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -2998,7 +2986,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) #endif PointedThing pointed = updatePointedThing(shootline, - playeritem_def.liquids_pointable, + selected_def.liquids_pointable, !runData.ldown_for_dig, camera_offset); @@ -3020,7 +3008,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) if (runData.digging) { if (input->getLeftReleased()) { infostream << "Left button released" - << " (stopped digging)" << std::endl; + << " (stopped digging)" << std::endl; runData.digging = false; } else if (pointed != runData.pointed_old) { if (pointed.type == POINTEDTHING_NODE @@ -3031,14 +3019,14 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) // Don't reset. } else { infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; + << " (stopped digging)" << std::endl; runData.digging = false; hud->updateSelectionMesh(camera_offset); } } if (!runData.digging) { - client->interact(1, runData.pointed_old); + client->interact(INTERACT_STOP_DIGGING, runData.pointed_old); client->setCrack(-1, v3s16(0, 0, 0)); runData.dig_time = 0.0; } @@ -3062,30 +3050,19 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) else runData.repeat_rightclick_timer = 0; - if (playeritem_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->moddingEnabled() - || !client->getScript()->on_item_use(playeritem, pointed))) - client->interact(4, pointed); + if (selected_def.usable && input->getLeftState()) { + if (input->getLeftClicked() && (!client->modsLoaded() + || !client->getScript()->on_item_use(selected_item, pointed))) + client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { - ToolCapabilities playeritem_toolcap = - playeritem.getToolCapabilities(itemdef_manager); - if (playeritem.name.empty()) { - const ToolCapabilities *handToolcap = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (handToolcap != nullptr) - playeritem_toolcap = *handToolcap; - } - handlePointingAtNode(pointed, playeritem_def, playeritem, - playeritem_toolcap, dtime); + handlePointingAtNode(pointed, selected_item, hand_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { - handlePointingAtObject(pointed, playeritem, player_position, show_debug); + handlePointingAtObject(pointed, tool_item, player_position, show_debug); } else if (input->getLeftState()) { // When button is held down in air, show continuous animation runData.left_punch = true; } else if (input->getRightClicked()) { - handlePointingAtNothing(playeritem); + handlePointingAtNothing(selected_item); } runData.pointed_old = pointed; @@ -3133,7 +3110,7 @@ PointedThing Game::updatePointedThing( } } else if (result.type == POINTEDTHING_NODE) { // Update selection boxes - MapNode n = map.getNodeNoEx(result.node_undersurface); + MapNode n = map.getNode(result.node_undersurface); std::vector<aabb3f> boxes; n.getSelectionBoxes(nodedef, &boxes, n.getNeighbors(result.node_undersurface, &map)); @@ -3160,12 +3137,12 @@ PointedThing Game::updatePointedThing( v3s16 p = floatToInt(pf, BS); // Get selection mesh light level - MapNode n = map.getNodeNoEx(p); + MapNode n = map.getNode(p); u16 node_light = getInteriorLight(n, -1, nodedef); u16 light_level = node_light; for (const v3s16 &dir : g_6dirs) { - n = map.getNodeNoEx(p + dir); + n = map.getNode(p + dir); node_light = getInteriorLight(n, -1, nodedef); if (node_light > light_level) light_level = node_light; @@ -3197,13 +3174,12 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem) infostream << "Right Clicked in Air" << std::endl; PointedThing fauxPointed; fauxPointed.type = POINTEDTHING_NOTHING; - client->interact(5, fauxPointed); + client->interact(INTERACT_ACTIVATE, fauxPointed); } void Game::handlePointingAtNode(const PointedThing &pointed, - const ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, f32 dtime) + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime) { v3s16 nodepos = pointed.node_undersurface; v3s16 neighbourpos = pointed.node_abovesurface; @@ -3217,7 +3193,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, if (runData.nodig_delay_timer <= 0.0 && input->getLeftState() && !runData.digging_blocked && client->checkPrivilege("interact")) { - handleDigging(pointed, nodepos, playeritem_toolcap, dtime); + handleDigging(pointed, nodepos, selected_item, hand_item, dtime); } // This should be done after digging handling @@ -3227,7 +3203,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, m_game_ui->setInfoText(unescape_translate(utf8_to_wide( meta->getString("infotext")))); } else { - MapNode n = map.getNodeNoEx(nodepos); + MapNode n = map.getNode(nodepos); if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") { m_game_ui->setInfoText(L"Unknown node: " + @@ -3241,213 +3217,219 @@ void Game::handlePointingAtNode(const PointedThing &pointed, runData.repeat_rightclick_timer = 0; infostream << "Ground right-clicked" << std::endl; - if (meta && !meta->getString("formspec").empty() && !random_input - && !isKeyDown(KeyType::SNEAK)) { - // Report right click to server - if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); - } - - infostream << "Launching custom inventory view" << std::endl; - - InventoryLocation inventoryloc; - inventoryloc.setNodeMeta(nodepos); + camera->setDigging(1); // right click animation (always shown for feedback) - NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( - &client->getEnv().getClientMap(), nodepos); - TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); + soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - cur_formname.clear(); + // If the wielded item has node placement prediction, + // make that happen + // And also set the sound and send the interact + // But first check for meta formspec and rightclickable + auto &def = selected_item.getDefinition(itemdef_manager); + bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos, + pointed, meta); - current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); - } else { - // Report right click to server - - camera->setDigging(1); // right click animation (always shown for feedback) - - // If the wielded item has node placement prediction, - // make that happen - bool placed = nodePlacementPrediction(playeritem_def, playeritem, nodepos, - neighbourpos); - - if (placed) { - // Report to server - client->interact(3, pointed); - // Read the sound - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place; - - if (client->moddingEnabled()) - client->getScript()->on_placenode(pointed, playeritem_def); - } else { - soundmaker->m_player_rightpunch_sound = - SimpleSoundSpec(); - - if (playeritem_def.node_placement_prediction.empty() || - nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); // Report to server - } else { - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place_failed; - } - } - } + if (placed && client->modsLoaded()) + client->getScript()->on_placenode(pointed, def); } } -bool Game::nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos) +bool Game::nodePlacement(const ItemDefinition &selected_def, + const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos, + const PointedThing &pointed, const NodeMetadata *meta) { - std::string prediction = playeritem_def.node_placement_prediction; + std::string prediction = selected_def.node_placement_prediction; const NodeDefManager *nodedef = client->ndef(); ClientMap &map = client->getEnv().getClientMap(); MapNode node; bool is_valid_position; - node = map.getNodeNoEx(nodepos, &is_valid_position); - if (!is_valid_position) + node = map.getNode(nodepos, &is_valid_position); + if (!is_valid_position) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; + } + + // formspec in meta + if (meta && !meta->getString("formspec").empty() && !random_input + && !isKeyDown(KeyType::SNEAK)) { + // on_rightclick callbacks are called anyway + if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) + client->interact(INTERACT_PLACE, pointed); + + infostream << "Launching custom inventory view" << std::endl; + + InventoryLocation inventoryloc; + inventoryloc.setNodeMeta(nodepos); + + NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( + &client->getEnv().getClientMap(), nodepos); + TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); + + auto *&formspec = m_game_ui->updateFormspec(""); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, + txt_dst, client->getFormspecPrepend()); + + formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; + } + + // on_rightclick callback + if (prediction.empty() || (nodedef->get(node).rightclickable && + !isKeyDown(KeyType::SNEAK))) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; + } - if (!prediction.empty() && !nodedef->get(node).rightclickable) { - verbosestream << "Node placement prediction for " - << playeritem_def.name << " is " - << prediction << std::endl; - v3s16 p = neighbourpos; - - // Place inside node itself if buildable_to - MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position); - if (is_valid_position) - { - if (nodedef->get(n_under).buildable_to) - p = nodepos; - else { - node = map.getNodeNoEx(p, &is_valid_position); - if (is_valid_position &&!nodedef->get(node).buildable_to) - return false; + verbosestream << "Node placement prediction for " + << selected_def.name << " is " + << prediction << std::endl; + v3s16 p = neighbourpos; + + // Place inside node itself if buildable_to + MapNode n_under = map.getNode(nodepos, &is_valid_position); + if (is_valid_position) { + if (nodedef->get(n_under).buildable_to) { + p = nodepos; + } else { + node = map.getNode(p, &is_valid_position); + if (is_valid_position && !nodedef->get(node).buildable_to) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; } } + } - // Find id of predicted node - content_t id; - bool found = nodedef->getId(prediction, id); + // Find id of predicted node + content_t id; + bool found = nodedef->getId(prediction, id); - if (!found) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Name not known" << std::endl; - return false; - } + if (!found) { + errorstream << "Node placement prediction failed for " + << selected_def.name << " (places " + << prediction + << ") - Name not known" << std::endl; + // Handle this as if prediction was empty + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; + } - const ContentFeatures &predicted_f = nodedef->get(id); + const ContentFeatures &predicted_f = nodedef->get(id); - // Predict param2 for facedir and wallmounted nodes - u8 param2 = 0; + // Predict param2 for facedir and wallmounted nodes + u8 param2 = 0; - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { - v3s16 dir = nodepos - neighbourpos; + v3s16 dir = nodepos - neighbourpos; - if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { - param2 = dir.Y < 0 ? 1 : 0; - } else if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 2; - } else { - param2 = dir.Z < 0 ? 5 : 4; - } + if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { + param2 = dir.Y < 0 ? 1 : 0; + } else if (abs(dir.X) > abs(dir.Z)) { + param2 = dir.X < 0 ? 3 : 2; + } else { + param2 = dir.Z < 0 ? 5 : 4; } + } - if (predicted_f.param_type_2 == CPT2_FACEDIR || + if (predicted_f.param_type_2 == CPT2_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { - v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); + v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); - if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 1; - } else { - param2 = dir.Z < 0 ? 2 : 0; - } + if (abs(dir.X) > abs(dir.Z)) { + param2 = dir.X < 0 ? 3 : 1; + } else { + param2 = dir.Z < 0 ? 2 : 0; } + } + + assert(param2 <= 5); - assert(param2 <= 5); - - //Check attachment if node is in group attached_node - if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { - static v3s16 wallmounted_dirs[8] = { - v3s16(0, 1, 0), - v3s16(0, -1, 0), - v3s16(1, 0, 0), - v3s16(-1, 0, 0), - v3s16(0, 0, 1), - v3s16(0, 0, -1), - }; - v3s16 pp; - - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + //Check attachment if node is in group attached_node + if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { + static v3s16 wallmounted_dirs[8] = { + v3s16(0, 1, 0), + v3s16(0, -1, 0), + v3s16(1, 0, 0), + v3s16(-1, 0, 0), + v3s16(0, 0, 1), + v3s16(0, 0, -1), + }; + v3s16 pp; + + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) - pp = p + wallmounted_dirs[param2]; - else - pp = p + v3s16(0, -1, 0); + pp = p + wallmounted_dirs[param2]; + else + pp = p + v3s16(0, -1, 0); - if (!nodedef->get(map.getNodeNoEx(pp)).walkable) - return false; + if (!nodedef->get(map.getNode(pp)).walkable) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; } + } - // Apply color - if ((predicted_f.param_type_2 == CPT2_COLOR + // Apply color + if ((predicted_f.param_type_2 == CPT2_COLOR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) { - const std::string &indexstr = playeritem.metadata.getString( - "palette_index", 0); - if (!indexstr.empty()) { - s32 index = mystoi(indexstr); - if (predicted_f.param_type_2 == CPT2_COLOR) { - param2 = index; - } else if (predicted_f.param_type_2 - == CPT2_COLORED_WALLMOUNTED) { - // param2 = pure palette index + other - param2 = (index & 0xf8) | (param2 & 0x07); - } else if (predicted_f.param_type_2 - == CPT2_COLORED_FACEDIR) { - // param2 = pure palette index + other - param2 = (index & 0xe0) | (param2 & 0x1f); - } + const std::string &indexstr = selected_item.metadata.getString( + "palette_index", 0); + if (!indexstr.empty()) { + s32 index = mystoi(indexstr); + if (predicted_f.param_type_2 == CPT2_COLOR) { + param2 = index; + } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { + // param2 = pure palette index + other + param2 = (index & 0xf8) | (param2 & 0x07); + } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { + // param2 = pure palette index + other + param2 = (index & 0xe0) | (param2 & 0x1f); } } + } - // Add node to client map - MapNode n(id, 0, param2); + // Add node to client map + MapNode n(id, 0, param2); - try { - LocalPlayer *player = client->getEnv().getLocalPlayer(); + try { + LocalPlayer *player = client->getEnv().getLocalPlayer(); - // Dont place node when player would be inside new node - // NOTE: This is to be eventually implemented by a mod as client-side Lua - if (!nodedef->get(n).walkable || + // Dont place node when player would be inside new node + // NOTE: This is to be eventually implemented by a mod as client-side Lua + if (!nodedef->get(n).walkable || g_settings->getBool("enable_build_where_you_stand") || (client->checkPrivilege("noclip") && g_settings->getBool("noclip")) || (nodedef->get(n).walkable && neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) && neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) { - - // This triggers the required mesh update too - client->addNode(p, n); - return true; - } - } catch (InvalidPositionException &e) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Position not loaded" << std::endl; + // This triggers the required mesh update too + client->addNode(p, n); + // Report to server + client->interact(INTERACT_PLACE, pointed); + // A node is predicted, also play a sound + soundmaker->m_player_rightpunch_sound = selected_def.sound_place; + return true; + } else { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; } + } catch (InvalidPositionException &e) { + errorstream << "Node placement prediction failed for " + << selected_def.name << " (places " + << prediction + << ") - Position not loaded" << std::endl; + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; } - - return false; } -void Game::handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, - const v3f &player_position, bool show_debug) +void Game::handlePointingAtObject(const PointedThing &pointed, + const ItemStack &tool_item, const v3f &player_position, bool show_debug) { std::wstring infotext = unescape_translate( utf8_to_wide(runData.selected_object->infoText())); @@ -3483,50 +3465,39 @@ void Game::handlePointingAtObject(const PointedThing &pointed, const ItemStack & // Report direct punch v3f objpos = runData.selected_object->getPosition(); v3f dir = (objpos - player_position).normalize(); - ItemStack item = playeritem; - if (playeritem.name.empty()) { - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) { - item = hlist->getItem(0); - } - } bool disable_send = runData.selected_object->directReportPunch( - dir, &item, runData.time_from_last_punch); + dir, &tool_item, runData.time_from_last_punch); runData.time_from_last_punch = 0; if (!disable_send) - client->interact(0, pointed); + client->interact(INTERACT_START_DIGGING, pointed); } } else if (input->getRightClicked()) { infostream << "Right-clicked object" << std::endl; - client->interact(3, pointed); // place + client->interact(INTERACT_PLACE, pointed); // place } } void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ToolCapabilities &playeritem_toolcap, f32 dtime) + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime) { + // See also: serverpackethandle.cpp, action == 2 LocalPlayer *player = client->getEnv().getLocalPlayer(); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos); + MapNode n = client->getEnv().getClientMap().getNode(nodepos); // NOTE: Similar piece of code exists on the server side for // cheat detection. // Get digging parameters DigParams params = getDigParams(nodedef_manager->get(n).groups, - &playeritem_toolcap); + &selected_item.getToolCapabilities(itemdef_manager)); // If can't dig, try hand if (!params.diggable) { - InventoryList *hlist = local_inventory->getList("hand"); - const ToolCapabilities *tp = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (tp) - params = getDigParams(nodedef_manager->get(n).groups, tp); + params = getDigParams(nodedef_manager->get(n).groups, + &hand_item.getToolCapabilities(itemdef_manager)); } if (!params.diggable) { @@ -3545,9 +3516,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, if (!runData.digging) { infostream << "Started digging" << std::endl; runData.dig_instantly = runData.dig_time_complete == 0; - if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n)) + if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n)) return; - client->interact(0, pointed); + client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; runData.ldown_for_dig = true; } @@ -3603,10 +3574,10 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, runData.nodig_delay_timer = 0.15; bool is_valid_position; - MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position); + MapNode wasnode = map.getNode(nodepos, &is_valid_position); if (is_valid_position) { - if (client->moddingEnabled() && - client->getScript()->on_dignode(nodepos, wasnode)) { + if (client->modsLoaded() && + client->getScript()->on_dignode(nodepos, wasnode)) { return; } @@ -3622,7 +3593,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, // implicit else: no prediction } - client->interact(2, pointed); + client->interact(INTERACT_DIGGING_COMPLETED, pointed); if (m_cache_enable_particles) { const ContentFeatures &features = @@ -3650,6 +3621,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam) { + TimeTaker tt_update("Game::updateFrame()"); LocalPlayer *player = client->getEnv().getLocalPlayer(); /* @@ -3674,7 +3646,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, direct_brightness = time_brightness; sunlight_seen = true; } else { - ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG); float old_brightness = sky->getBrightness(); direct_brightness = client->getEnv().getClientMap() .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), @@ -3780,31 +3751,14 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, Inventory */ - if (client->getPlayerItem() != runData.new_playeritem) - client->selectPlayerItem(runData.new_playeritem); + if (player->getWieldIndex() != runData.new_playeritem) + client->setPlayerItem(runData.new_playeritem); - // Update local inventory if it has changed - if (client->getLocalInventoryUpdated()) { - //infostream<<"Updating local inventory"<<std::endl; - client->getLocalInventory(*local_inventory); - runData.update_wielded_item_trigger = true; - } - - if (runData.update_wielded_item_trigger) { + if (client->updateWieldedItem()) { // Update wielded tool - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && (client->getPlayerItem() < mlist->getSize())) { - ItemStack item = mlist->getItem(client->getPlayerItem()); - if (item.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - item = hlist->getItem(0); - } - camera->wield(item); - } - - runData.update_wielded_item_trigger = false; + ItemStack selected_item, hand_item; + ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); + camera->wield(tool_item); } /* @@ -3822,28 +3776,42 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.update_draw_list_last_cam_dir = camera_direction; } - m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, dtime); + m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); /* make sure menu is on top 1. Delete formspec menu reference if menu was removed 2. Else, make sure formspec menu is on top */ - if (current_formspec) { - if (current_formspec->getReferenceCount() == 1) { - current_formspec->drop(); - current_formspec = NULL; - } else if (isMenuActive()) { - guiroot->bringToFront(current_formspec); + auto formspec = m_game_ui->getFormspecGUI(); + do { // breakable. only runs for one iteration + if (!formspec) + break; + + if (formspec->getReferenceCount() == 1) { + m_game_ui->deleteFormspec(); + break; } - } + + auto &loc = formspec->getFormspecLocation(); + if (loc.type == InventoryLocation::NODEMETA) { + NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p); + if (!meta || meta->getString("formspec").empty()) { + formspec->quitMenu(); + break; + } + } + + if (isMenuActive()) + guiroot->bringToFront(formspec); + } while (false); /* Drawing begins */ const video::SColor &skycolor = sky->getSkyColor(); - TimeTaker tt_draw("mainloop: draw"); + TimeTaker tt_draw("Draw scene"); driver->beginScene(true, true, skycolor); bool draw_wield_tool = (m_game_ui->m_flags.show_hud && @@ -3903,7 +3871,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, driver->endScene(); stats->drawtime = tt_draw.stop(true); - g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f); + g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime); + g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true)); } /* Log times and stuff for visualization */ @@ -4033,8 +4002,8 @@ void Game::extendedResourceCleanup() void Game::showDeathFormspec() { - static std::string formspec = - std::string(FORMSPEC_VERSION_STRING) + + static std::string formspec_str = + std::string("formspec_version[1]") + SIZE_TAG "bgcolor[#320000b4;true]" "label[4.85,1.35;" + gettext("You died") + "]" @@ -4044,12 +4013,13 @@ void Game::showDeathFormspec() /* Create menu */ /* Note: FormspecFormSource and LocalFormspecHandler * * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec); + FormspecFormSource *fs_src = new FormspecFormSource(formspec_str); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_respawn"); + auto *&formspec = m_game_ui->getFormspecGUI(); + GUIFormSpecMenu::create(formspec, client, &input->joystick, + fs_src, txt_dst, client->getFormspecPrepend()); + formspec->setFocus("btn_respawn"); } #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name()) @@ -4107,7 +4077,7 @@ void Game::showPauseMenu() float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; std::ostringstream os; - os << FORMSPEC_VERSION_STRING << SIZE_TAG + os << "formspec_version[1]" << SIZE_TAG << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" << strgettext("Continue") << "]"; @@ -4173,10 +4143,11 @@ void Game::showPauseMenu() FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + auto *&formspec = m_game_ui->getFormspecGUI(); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_continue"); - current_formspec->doPause = true; + formspec->setFocus("btn_continue"); + formspec->doPause = true; } /****************************************************************************/ @@ -4227,7 +4198,8 @@ void the_game(bool *kill, error_message = e.what(); errorstream << "ServerError: " << error_message << std::endl; } catch (ModError &e) { - error_message = e.what() + strgettext("\nCheck debug.txt for details."); - errorstream << "ModError: " << error_message << std::endl; + error_message = std::string("ModError: ") + e.what() + + strgettext("\nCheck debug.txt for details."); + errorstream << error_message << std::endl; } } diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 1f433e49a..674d07fa6 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <irrlicht_changes/static_text.h> #include <gettext.h> #include "gui/mainmenumanager.h" +#include "gui/guiChatConsole.h" #include "util/pointedthing.h" #include "client.h" #include "clientmap.h" @@ -79,13 +80,15 @@ void GameUI::init() // 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); + m_guitext_profiler->setOverrideFont(g_fontengine->getFont( + g_fontengine->getDefaultFontSize() * 0.9f, FM_Mono)); m_guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0)); m_guitext_profiler->setVisible(false); - m_guitext_profiler->setWordWrap(true); } void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_control, - const CameraOrientation &cam, const PointedThing &pointed_old, float dtime) + const CameraOrientation &cam, const PointedThing &pointed_old, + const GUIChatConsole *chat_console, float dtime) { v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); @@ -97,17 +100,17 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ std::ostringstream os(std::ios_base::binary); os << std::fixed << PROJECT_NAME_C " " << g_version_hash - << ", FPS: " << fps + << " | FPS: " << fps << std::setprecision(0) - << ", drawtime: " << drawtime_avg << "ms" + << " | drawtime: " << drawtime_avg << "ms" << std::setprecision(1) - << ", dtime jitter: " + << " | dtime jitter: " << (stats.dtime_jitter.max_fraction * 100.0) << "%" << std::setprecision(1) - << ", view range: " + << " | view range: " << (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) << std::setprecision(3) - << ", RTT: " << client->getRTT() << "s"; + << " | RTT: " << client->getRTT() << "s"; setStaticText(m_guitext, utf8_to_wide(os.str()).c_str()); m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X, @@ -126,14 +129,15 @@ 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)) << "° " + << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° " << yawToDirectionString(cam.camera_yaw) - << ", seed: " << ((u64)client->getMapSeed()); + << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°" + << " | seed: " << ((u64)client->getMapSeed()); if (pointed_old.type == POINTEDTHING_NODE) { ClientMap &map = client->getEnv().getClientMap(); const NodeDefManager *nodedef = client->getNodeDefManager(); - MapNode n = map.getNodeNoEx(pointed_old.node_undersurface); + MapNode n = map.getNode(pointed_old.node_undersurface); if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") { os << ", pointed: " << nodedef->get(n).name @@ -185,6 +189,9 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ m_guitext_status->setOverrideColor(fade_color); m_guitext_status->enableOverrideColor(true); } + + // Hide chat when console is visible + m_guitext_chat->setVisible(isChatVisible() && !chat_console->isVisible()); } void GameUI::initFlags() @@ -226,38 +233,28 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width, chat_y + m_guitext_chat->getTextHeight())); - // Don't show chat if disabled or empty or profiler is enabled - m_guitext_chat->setVisible(m_flags.show_chat && - recent_chat_count != 0 && m_profiler_current_page == 0); + m_recent_chat_count = recent_chat_count; } void GameUI::updateProfiler() { if (m_profiler_current_page != 0) { std::ostringstream os(std::ios_base::binary); - g_profiler->printPage(os, m_profiler_current_page, m_profiler_max_page); - - std::wstring text = translate_string(utf8_to_wide(os.str())); - setStaticText(m_guitext_profiler, text.c_str()); - - s32 w = g_fontengine->getTextWidth(text); - - if (w < 400) - w = 400; - - u32 text_height = g_fontengine->getTextHeight(); - - core::position2di upper_left, lower_right; + os << " Profiler page " << (int)m_profiler_current_page << + ", elapsed: " << g_profiler->getElapsedMs() << " ms)" << std::endl; - upper_left.X = 6; - upper_left.Y = (text_height + 5) * 2; - lower_right.X = 12 + w; - lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS; + int lines = g_profiler->print(os, m_profiler_current_page, m_profiler_max_page); + ++lines; - s32 screen_height = RenderingEngine::get_video_driver()->getScreenSize().Height; + std::wstring text = utf8_to_wide(os.str()); + setStaticText(m_guitext_profiler, text.c_str()); - if (lower_right.Y > screen_height * 2 / 3) - lower_right.Y = screen_height * 2 / 3; + core::dimension2d<u32> size = m_guitext_profiler->getOverrideFont()-> + getDimension(text.c_str()); + core::position2di upper_left(6, 50); + core::position2di lower_right = upper_left; + lower_right.X += size.Width + 10; + lower_right.Y += size.Height; m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right)); } @@ -301,3 +298,14 @@ void GameUI::toggleProfiler() showTranslatedStatusText("Profiler hidden"); } } + + +void GameUI::deleteFormspec() +{ + if (m_formspec) { + m_formspec->drop(); + m_formspec = nullptr; + } + + m_formname.clear(); +} diff --git a/src/client/gameui.h b/src/client/gameui.h index b6b54562a..67c6a9921 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -21,12 +21,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include <IGUIEnvironment.h> +#include "gui/guiFormSpecMenu.h" #include "util/enriched_string.h" #include "util/pointedthing.h" #include "game.h" using namespace irr; class Client; +class GUIChatConsole; struct MapDrawControl; /* @@ -62,7 +64,7 @@ public: void init(); void update(const RunStats &stats, Client *client, MapDrawControl *draw_control, const CameraOrientation &cam, const PointedThing &pointed_old, - float dtime); + const GUIChatConsole *chat_console, float dtime); void initFlags(); const Flags &getFlags() const { return m_flags; } @@ -80,6 +82,10 @@ public: void showTranslatedStatusText(const char *str); inline void clearStatusText() { m_statustext.clear(); } + const bool isChatVisible() + { + return m_flags.show_chat && m_recent_chat_count != 0 && m_profiler_current_page == 0; + } void setChatText(const EnrichedString &chat_text, u32 recent_chat_count); void updateProfiler(); @@ -88,6 +94,16 @@ public: void toggleHud(); void toggleProfiler(); + GUIFormSpecMenu *&updateFormspec(const std::string &formname) + { + m_formname = formname; + return m_formspec; + } + + const std::string &getFormspecName() { return m_formname; } + GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; } + void deleteFormspec(); + private: Flags m_flags; @@ -103,8 +119,14 @@ private: video::SColor m_statustext_initial_color; gui::IGUIStaticText *m_guitext_chat = nullptr; // Chat text + u32 m_recent_chat_count = 0; gui::IGUIStaticText *m_guitext_profiler = nullptr; // Profiler text u8 m_profiler_current_page = 0; const u8 m_profiler_max_page = 3; + + // Default: "". If other than "": Empty show_formspec packets will only + // close the formspec when the formname matches + std::string m_formname; + GUIFormSpecMenu *m_formspec = nullptr; }; diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index 3b4377da5..2ff57ab74 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include <cstdio> #include "client/renderingengine.h" +#include "client/tile.h" // hasNPotSupport() /* Maintain a static cache to store the images that correspond to textures * in a format that's manipulable by code. Some platforms exhibit issues @@ -39,7 +40,7 @@ std::map<io::path, video::ITexture *> g_txrCache; /* Manually insert an image into the cache, useful to avoid texture-to-image * conversion whenever we can intercept it. */ -void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value) +void guiScalingCache(const io::path &key, video::IVideoDriver *driver, video::IImage *value) { if (!g_settings->getBool("gui_scaling_filter")) return; @@ -113,17 +114,18 @@ video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, (u32)destrect.getHeight())); imageScaleNNAA(srcimg, srcrect, destimg); -#ifdef __ANDROID__ - // Android is very picky about textures being powers of 2, so expand - // the image dimensions to the next power of 2, if necessary, for - // that platform. - video::IImage *po2img = driver->createImage(src->getColorFormat(), - core::dimension2d<u32>(npot2((u32)destrect.getWidth()), - npot2((u32)destrect.getHeight()))); - po2img->fill(video::SColor(0, 0, 0, 0)); - destimg->copyTo(po2img); - destimg->drop(); - destimg = po2img; +#if ENABLE_GLES + // Some platforms are picky about textures being powers of 2, so expand + // the image dimensions to the next power of 2, if necessary. + if (!hasNPotSupport()) { + video::IImage *po2img = driver->createImage(src->getColorFormat(), + core::dimension2d<u32>(npot2((u32)destrect.getWidth()), + npot2((u32)destrect.getHeight()))); + po2img->fill(video::SColor(0, 0, 0, 0)); + destimg->copyTo(po2img); + destimg->drop(); + destimg = po2img; + } #endif // Convert the scaled image back into a texture. @@ -167,3 +169,62 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, driver->draw2DImage(scaled, destrect, mysrcrect, cliprect, colors, usealpha); } + +void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, + const core::rect<s32> &rect, const core::rect<s32> &middle) +{ + 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; + + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 3; ++x) { + core::rect<s32> src({0, 0}, originalSize); + core::rect<s32> dest = rect; + + switch (x) { + case 0: + dest.LowerRightCorner.X = rect.UpperLeftCorner.X + middle.UpperLeftCorner.X; + src.LowerRightCorner.X = middle.UpperLeftCorner.X; + break; + + case 1: + dest.UpperLeftCorner.X += middle.UpperLeftCorner.X; + dest.LowerRightCorner.X -= lowerRightOffset.X; + src.UpperLeftCorner.X = middle.UpperLeftCorner.X; + src.LowerRightCorner.X = middle.LowerRightCorner.X; + break; + + case 2: + dest.UpperLeftCorner.X = rect.LowerRightCorner.X - lowerRightOffset.X; + src.UpperLeftCorner.X = middle.LowerRightCorner.X; + break; + } + + switch (y) { + case 0: + dest.LowerRightCorner.Y = rect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y; + src.LowerRightCorner.Y = middle.UpperLeftCorner.Y; + break; + + case 1: + dest.UpperLeftCorner.Y += middle.UpperLeftCorner.Y; + dest.LowerRightCorner.Y -= lowerRightOffset.Y; + src.UpperLeftCorner.Y = middle.UpperLeftCorner.Y; + src.LowerRightCorner.Y = middle.LowerRightCorner.Y; + break; + + case 2: + dest.UpperLeftCorner.Y = rect.LowerRightCorner.Y - lowerRightOffset.Y; + src.UpperLeftCorner.Y = middle.LowerRightCorner.Y; + break; + } + + draw2DImageFilterScaled(driver, texture, dest, + src, + NULL/*&AbsoluteClippingRect*/, colors, true); + } + } +} diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index 4661bf8da..181009551 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Manually insert an image into the cache, useful to avoid texture-to-image * conversion whenever we can intercept it. */ -void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value); +void guiScalingCache(const io::path &key, video::IVideoDriver *driver, video::IImage *value); // Manually clear the cache, e.g. when switching to different worlds. void guiScalingCacheClear(); @@ -48,3 +48,9 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, const core::rect<s32> &destrect, const core::rect<s32> &srcrect, const core::rect<s32> *cliprect = 0, const video::SColor *const colors = 0, bool usealpha = false); + +/* + * 9-slice / segment drawing + */ +void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, + const core::rect<s32> &rect, const core::rect<s32> &middle); diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 1a2287a13..291d03816 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -372,7 +372,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } -void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, +void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, s32 count, v2s32 offset, v2s32 size) { const video::SColor color(255, 255, 255, 255); @@ -649,10 +649,31 @@ void drawItemStack(video::IVideoDriver *driver, core::rect<s32> oldViewPort = driver->getViewPort(); core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + core::rect<s32> viewrect = rect; + if (clip) + viewrect.clipAgainst(*clip); + core::matrix4 ProjMatrix; - ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100); + ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f); + + core::matrix4 ViewMatrix; + ViewMatrix.buildProjectionMatrixOrthoLH( + 2.0f * viewrect.getWidth() / rect.getWidth(), + 2.0f * viewrect.getHeight() / rect.getHeight(), + -1.0f, + 100.0f); + ViewMatrix.setTranslation(core::vector3df( + 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X - + viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) / + viewrect.getWidth(), + 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y - + rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) / + viewrect.getHeight(), + 0.0f)); + driver->setTransform(video::ETS_PROJECTION, ProjMatrix); - driver->setTransform(video::ETS_VIEW, ProjMatrix); + driver->setTransform(video::ETS_VIEW, ViewMatrix); + core::matrix4 matrix; matrix.makeIdentity(); @@ -662,7 +683,7 @@ void drawItemStack(video::IVideoDriver *driver, } driver->setTransform(video::ETS_WORLD, matrix); - driver->setViewPort(rect); + driver->setViewPort(viewrect); video::SColor basecolor = client->idef()->getItemstackColor(item, client); @@ -693,6 +714,16 @@ void drawItemStack(video::IVideoDriver *driver, driver->setTransform(video::ETS_VIEW, oldViewMat); driver->setTransform(video::ETS_PROJECTION, oldProjMat); driver->setViewPort(oldViewPort); + + // draw the inventory_overlay + if (def.type == ITEM_NODE && def.inventory_image.empty() && + !def.inventory_overlay.empty()) { + ITextureSource *tsrc = client->getTextureSource(); + video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay); + core::dimension2d<u32> dimens = overlay_texture->getOriginalSize(); + core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height); + draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true); + } } if(def.type == ITEM_TOOL && item.wear != 0) diff --git a/src/client/hud.h b/src/client/hud.h index e9bcdf4e2..693d2adee 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -81,7 +81,7 @@ public: void drawLuaElements(const v3s16 &camera_offset); private: - void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, + void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, s32 count, v2s32 offset, v2s32 size = v2s32()); void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 7e7b1a867..c086d860a 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -41,7 +41,7 @@ LocalPlayer::LocalPlayer(Client *client, const char *name): static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes) { if (nodeboxes.empty()) - return aabb3f(0, 0, 0, 0, 0, 0); + return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); aabb3f b_max; @@ -56,7 +56,7 @@ static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes) } bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, - const v3f &sneak_max) + const v3f &sneak_max) { static const v3s16 dir9_center[9] = { v3s16( 0, 0, 0), @@ -76,17 +76,17 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, bool new_sneak_node_exists = m_sneak_node_exists; // We want the top of the sneak node to be below the players feet - f32 position_y_mod = 0.05 * BS; + f32 position_y_mod = 0.05f * BS; if (m_sneak_node_exists) position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod; // Get position of current standing node - const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS); + const v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS); if (current_node != m_sneak_node) { new_sneak_node_exists = false; } else { - node = map->getNodeNoEx(current_node, &is_valid_position); + node = map->getNode(current_node, &is_valid_position); if (!is_valid_position || !nodemgr->get(node).walkable) new_sneak_node_exists = false; } @@ -97,7 +97,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, // Get new sneak node m_sneak_ladder_detected = false; - f32 min_distance_f = 100000.0 * BS; + f32 min_distance_f = 100000.0f * BS; for (const auto &d : dir9_center) { const v3s16 p = current_node + d; @@ -106,23 +106,22 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, f32 distance_f = diff.getLength(); if (distance_f > min_distance_f || - fabs(diff.X) > (.5 + .1) * BS + sneak_max.X || - fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z) + fabs(diff.X) > (0.5f + 0.1f) * BS + sneak_max.X || + fabs(diff.Y) > (0.5f + 0.1f) * BS + sneak_max.Z) continue; // The node to be sneaked on has to be walkable - node = map->getNodeNoEx(p, &is_valid_position); + node = map->getNode(p, &is_valid_position); if (!is_valid_position || !nodemgr->get(node).walkable) continue; // And the node(s) above have to be nonwalkable bool ok = true; if (!physics_override_sneak_glitch) { - u16 height = ceilf( - (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS - ); + u16 height = + ceilf((m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS); for (u16 y = 1; y <= height; y++) { - node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position); + node = map->getNode(p + v3s16(0, y, 0), &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable) { ok = false; break; @@ -130,7 +129,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, } } else { // legacy behaviour: check just one node - node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position); + node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position); ok = is_valid_position && !nodemgr->get(node).walkable; } if (!ok) @@ -145,7 +144,7 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, return false; // Update saved top bounding box of sneak node - node = map->getNodeNoEx(m_sneak_node); + node = map->getNode(m_sneak_node); std::vector<aabb3f> nodeboxes; node.getCollisionBoxes(nodemgr, &nodeboxes); m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes); @@ -153,11 +152,11 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, if (physics_override_sneak_glitch) { // Detect sneak ladder: // Node two meters above sneak node must be solid - node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0), + node = map->getNode(m_sneak_node + v3s16(0, 2, 0), &is_valid_position); if (is_valid_position && nodemgr->get(node).walkable) { // Node three meters above: must be non-solid - node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0), + node = map->getNode(m_sneak_node + v3s16(0, 3, 0), &is_valid_position); m_sneak_ladder_detected = is_valid_position && !nodemgr->get(node).walkable; @@ -169,10 +168,9 @@ bool LocalPlayer::updateSneakNode(Map *map, const v3f &position, void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector<CollisionInfo> *collision_info) { - if (!collision_info || collision_info->empty()) { - // Node below the feet, update each ClientEnvironment::step() - m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0); - } + // Node at feet position, update each ClientEnvironment::step() + if (!collision_info || collision_info->empty()) + m_standing_node = floatToInt(m_position, BS); // Temporary option for old move code if (!physics_override_new_move) { @@ -188,6 +186,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // Copy parent position if local player is attached if (isAttached) { setPosition(overridePosition); + added_velocity = v3f(0.0f); // ignored return; } @@ -201,9 +200,13 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (noclip && free_move) { position += m_speed * dtime; setPosition(position); + added_velocity = v3f(0.0f); // ignored return; } + m_speed += added_velocity; + added_velocity = v3f(0.0f); + /* Collision detection */ @@ -219,20 +222,19 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // If in liquid, the threshold of coming out is at higher y if (in_liquid) { - pp = floatToInt(position + v3f(0,BS*0.1,0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; } else { in_liquid = false; } - } - // If not in liquid, the threshold of going in is at lower y - else - { - pp = floatToInt(position + v3f(0,BS*0.5,0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + } else { + // If not in liquid, the threshold of going in is at lower y + + pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; @@ -245,8 +247,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, /* Check if player is in liquid (the stable value) */ - pp = floatToInt(position + v3f(0,0,0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) { in_liquid_stable = nodemgr->get(node.getContent()).isLiquid(); } else { @@ -254,21 +256,20 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } /* - Check if player is climbing + Check if player is climbing */ - - pp = floatToInt(position + v3f(0,0.5*BS,0), BS); - v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS); + v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); bool is_valid_position2; - MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2); + MapNode node2 = map->getNode(pp2, &is_valid_position2); if (!(is_valid_position && is_valid_position2)) { is_climbing = false; } else { - is_climbing = (nodemgr->get(node.getContent()).climbable - || nodemgr->get(node2.getContent()).climbable) && !free_move; + is_climbing = (nodemgr->get(node.getContent()).climbable || + nodemgr->get(node2.getContent()).climbable) && !free_move; } /* @@ -277,7 +278,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, */ //f32 d = pos_max_d * 1.1; // A fairly large value in here makes moving smoother - f32 d = 0.15*BS; + f32 d = 0.15f * BS; // This should always apply, otherwise there are glitches sanity_check(d > pos_max_d); @@ -287,7 +288,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, float player_stepheight = (m_cao == nullptr) ? 0.0f : (touching_ground ? m_cao->getStepHeight() : (0.2f * BS)); - v3f accel_f = v3f(0,0,0); + v3f accel_f; const v3f initial_position = position; const v3f initial_speed = m_speed; @@ -309,7 +310,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, collision_info->push_back(colinfo); if (colinfo.type != COLLISION_NODE || - colinfo.new_speed.Y != 0 || + colinfo.axis != COLLISION_AXIS_Y || (could_sneak && m_sneak_node_exists)) continue; @@ -320,6 +321,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (is_first || len < distance) { m_standing_node = colinfo.node_p; distance = len; + is_first = false; } } } @@ -340,7 +342,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (m_sneak_ladder_detected) { // restore legacy behaviour (this makes the m_speed.Y hack necessary) - sneak_max = v3f(0.4 * BS, 0, 0.4 * BS); + sneak_max = v3f(0.4f * BS, 0.0f, 0.4f * BS); } /* @@ -364,12 +366,12 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z); if (position.X != old_pos.X) - m_speed.X = 0; + m_speed.X = 0.0f; if (position.Z != old_pos.Z) - m_speed.Z = 0; + m_speed.Z = 0.0f; } - if (y_diff > 0 && m_speed.Y <= 0 && + if (y_diff > 0 && m_speed.Y <= 0.0f && (physics_override_sneak_glitch || y_diff < BS * 0.6f)) { // Move player to the maximal height when falling or when // the ledge is climbed on the next step. @@ -377,11 +379,11 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // Smoothen the movement (based on 'position.Y = bmax.Y') position.Y += y_diff * dtime * 22.0f + BS * 0.01f; position.Y = std::min(position.Y, bmax.Y); - m_speed.Y = 0; + m_speed.Y = 0.0f; } // Allow jumping on node edges while sneaking - if (m_speed.Y == 0 || m_sneak_ladder_detected) + if (m_speed.Y == 0.0f || m_sneak_ladder_detected) sneak_can_jump = true; if (collision_info && @@ -413,7 +415,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, Report collisions */ - if(!result.standing_on_object && !touching_ground_was && touching_ground) { + if (!result.standing_on_object && !touching_ground_was && touching_ground) { m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND)); // Set camera impact value to be used for view bobbing @@ -423,31 +425,29 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, { camera_barely_in_ceiling = false; v3s16 camera_np = floatToInt(getEyePosition(), BS); - MapNode n = map->getNodeNoEx(camera_np); - if(n.getContent() != CONTENT_IGNORE){ - if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){ + MapNode n = map->getNode(camera_np); + if (n.getContent() != CONTENT_IGNORE) { + if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2) camera_barely_in_ceiling = true; - } } } /* Check properties of the node on which the player is standing */ - const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node)); + const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node)); + // Determine if jumping is possible - m_can_jump = (touching_ground && !in_liquid && !is_climbing) - || sneak_can_jump; - if (itemgroup_get(f.groups, "disable_jump")) - m_can_jump = false; + m_disable_jump = itemgroup_get(f.groups, "disable_jump"); + m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump; // Jump key pressed while jumping off from a bouncy block if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") && - m_speed.Y >= -0.5 * BS) { + m_speed.Y >= -0.5f * BS) { float jumpspeed = movement_speed_jump * physics_override_jump; - if (m_speed.Y > 1) { + if (m_speed.Y > 1.0f) { // Reduce boost when speed already is high - m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 )); + m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f)); } else { m_speed.Y += jumpspeed; } @@ -474,19 +474,17 @@ void LocalPlayer::applyControl(float dtime, Environment *env) setYaw(control.yaw); // Nullify speed and don't run positioning code if the player is attached - if(isAttached) - { - setSpeed(v3f(0,0,0)); + if (isAttached) { + setSpeed(v3f(0.0f)); return; } PlayerSettings &player_settings = getPlayerSettings(); // All vectors are relative to the player's yaw, - // (and pitch if pitch fly mode enabled), + // (and pitch if pitch move mode enabled), // and will be rotated at the end - v3f speedH = v3f(0,0,0); // Horizontal (X, Z) - v3f speedV = v3f(0,0,0); // Vertical (Y) + v3f speedH, speedV; // Horizontal (X, Z) and Vertical (Y) bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool fast_allowed = m_client->checkLocalPrivilege("fast"); @@ -496,7 +494,6 @@ void LocalPlayer::applyControl(float dtime, Environment *env) bool pitch_move = (free_move || in_liquid) && player_settings.pitch_move; // When aux1_descends is enabled the fast key is used to go down, so fast isn't possible bool fast_climb = fast_move && control.aux1 && !player_settings.aux1_descends; - bool continuous_forward = player_settings.continuous_forward; bool always_fly_fast = player_settings.always_fly_fast; // Whether superspeed mode is used or not @@ -506,76 +503,58 @@ void LocalPlayer::applyControl(float dtime, Environment *env) superspeed = true; // Old descend control - if (player_settings.aux1_descends) - { + if (player_settings.aux1_descends) { // If free movement and fast movement, always move fast - if(free_move && fast_move) + if (free_move && fast_move) superspeed = true; // Auxiliary button 1 (E) - if(control.aux1) - { - if(free_move) - { + if (control.aux1) { + if (free_move) { // In free movement mode, aux1 descends - if(fast_move) + if (fast_move) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_walk; - } - else if(in_liquid || in_liquid_stable) - { + } else if (in_liquid || in_liquid_stable) { speedV.Y = -movement_speed_walk; swimming_vertical = true; - } - else if(is_climbing) - { + } else if (is_climbing) { speedV.Y = -movement_speed_climb; - } - else - { + } else { // If not free movement but fast is allowed, aux1 is // "Turbo button" - if(fast_move) + if (fast_move) superspeed = true; } } - } - // New minecraft-like descend control - else - { + } else { + // New minecraft-like descend control + // Auxiliary button 1 (E) - if(control.aux1) - { - if(!is_climbing) - { + if (control.aux1) { + if (!is_climbing) { // aux1 is "Turbo button" - if(fast_move) + if (fast_move) superspeed = true; } } - if(control.sneak) - { - if(free_move) - { + if (control.sneak) { + if (free_move) { // In free movement mode, sneak descends if (fast_move && (control.aux1 || always_fly_fast)) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_walk; - } - else if(in_liquid || in_liquid_stable) - { - if(fast_climb) + } else if (in_liquid || in_liquid_stable) { + if (fast_climb) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_walk; swimming_vertical = true; - } - else if(is_climbing) - { - if(fast_climb) + } else if (is_climbing) { + if (fast_climb) speedV.Y = -movement_speed_fast; else speedV.Y = -movement_speed_climb; @@ -583,42 +562,32 @@ void LocalPlayer::applyControl(float dtime, Environment *env) } } - if (continuous_forward) - speedH += v3f(0,0,1); + if (control.up) + speedH += v3f(0.0f, 0.0f, 1.0f); + + if (control.down) + speedH -= v3f(0.0f, 0.0f, 1.0f); + + if (!control.up && !control.down) + speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f); + + if (control.left) + speedH += v3f(-1.0f, 0.0f, 0.0f); + + if (control.right) + speedH += v3f(1.0f, 0.0f, 0.0f); + + if (!control.left && !control.right) + speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f); - if (control.up) { - if (continuous_forward) { - if (fast_move) - superspeed = true; - } else { - speedH += v3f(0,0,1); - } - } - if (control.down) { - speedH -= v3f(0,0,1); - } - if (!control.up && !control.down) { - speedH -= v3f(0,0,1) * - (control.forw_move_joystick_axis / 32767.f); - } - if (control.left) { - speedH += v3f(-1,0,0); - } - if (control.right) { - speedH += v3f(1,0,0); - } - if (!control.left && !control.right) { - speedH += v3f(1,0,0) * - (control.sidew_move_joystick_axis / 32767.f); - } if (m_autojump) { // release autojump after a given time m_autojump_time -= dtime; if (m_autojump_time <= 0.0f) m_autojump = false; } - if(control.jump) - { + + if (control.jump) { if (free_move) { if (player_settings.aux1_descends || always_fly_fast) { if (fast_move) @@ -626,37 +595,31 @@ void LocalPlayer::applyControl(float dtime, Environment *env) else speedV.Y = movement_speed_walk; } else { - if(fast_move && control.aux1) + if (fast_move && control.aux1) speedV.Y = movement_speed_fast; else speedV.Y = movement_speed_walk; } - } - else if(m_can_jump) - { + } else if (m_can_jump) { /* NOTE: The d value in move() affects jump height by raising the height at which the jump speed is kept at its starting value */ v3f speedJ = getSpeed(); - if(speedJ.Y >= -0.5 * BS) { + if (speedJ.Y >= -0.5f * BS) { speedJ.Y = movement_speed_jump * physics_override_jump; setSpeed(speedJ); m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_JUMP)); } - } - else if(in_liquid) - { - if(fast_climb) + } else if (in_liquid && !m_disable_jump) { + if (fast_climb) speedV.Y = movement_speed_fast; else speedV.Y = movement_speed_walk; swimming_vertical = true; - } - else if(is_climbing) - { - if(fast_climb) + } else if (is_climbing && !m_disable_jump) { + if (fast_climb) speedV.Y = movement_speed_fast; else speedV.Y = movement_speed_climb; @@ -664,29 +627,31 @@ void LocalPlayer::applyControl(float dtime, Environment *env) } // The speed of the player (Y is ignored) - if(superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb)) + if (superspeed || (is_climbing && fast_climb) || + ((in_liquid || in_liquid_stable) && fast_climb)) speedH = speedH.normalize() * movement_speed_fast; - else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable) + else if (control.sneak && !free_move && !in_liquid && !in_liquid_stable) speedH = speedH.normalize() * movement_speed_crouch; else speedH = speedH.normalize() * movement_speed_walk; // Acceleration increase - f32 incH = 0; // Horizontal (X, Z) - f32 incV = 0; // Vertical (Y) - if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump)) - { + f32 incH = 0.0f; // Horizontal (X, Z) + f32 incV = 0.0f; // Vertical (Y) + if ((!touching_ground && !free_move && !is_climbing && !in_liquid) || + (!free_move && m_can_jump && control.jump)) { // Jumping and falling - if(superspeed || (fast_move && control.aux1)) + if (superspeed || (fast_move && control.aux1)) incH = movement_acceleration_fast * BS * dtime; else incH = movement_acceleration_air * BS * dtime; - incV = 0; // No vertical acceleration in air - } - else if (superspeed || (is_climbing && fast_climb) || ((in_liquid || in_liquid_stable) && fast_climb)) + incV = 0.0f; // No vertical acceleration in air + } else if (superspeed || (is_climbing && fast_climb) || + ((in_liquid || in_liquid_stable) && fast_climb)) { incH = incV = movement_acceleration_fast * BS * dtime; - else + } else { incH = incV = movement_acceleration_default * BS * dtime; + } float slip_factor = 1.0f; if (!free_move && !in_liquid && !in_liquid_stable) @@ -701,47 +666,55 @@ void LocalPlayer::applyControl(float dtime, Environment *env) // Accelerate to target speed with maximum increment accelerate((speedH + speedV) * physics_override_speed, - incH * physics_override_speed * slip_factor, incV * physics_override_speed, - pitch_move); + incH * physics_override_speed * slip_factor, incV * physics_override_speed, + pitch_move); } v3s16 LocalPlayer::getStandingNodePos() { - if(m_sneak_node_exists) + if (m_sneak_node_exists) return m_sneak_node; + return m_standing_node; } v3s16 LocalPlayer::getFootstepNodePos() { + // Emit swimming sound if the player is in liquid if (in_liquid_stable) - // Emit swimming sound if the player is in liquid return floatToInt(getPosition(), BS); + + // BS * 0.05 below the player's feet ensures a 1/16th height + // nodebox is detected instead of the node below it. if (touching_ground) - // BS * 0.05 below the player's feet ensures a 1/16th height - // nodebox is detected instead of the node below it. - return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS); + return floatToInt(getPosition() - v3f(0.0f, BS * 0.05f, 0.0f), BS); + // A larger distance below is necessary for a footstep sound // when landing after a jump or fall. BS * 0.5 ensures water // sounds when swimming in 1 node deep water. - return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS); + return floatToInt(getPosition() - v3f(0.0f, BS * 0.5f, 0.0f), BS); } v3s16 LocalPlayer::getLightPosition() const { - return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); + return floatToInt(m_position + v3f(0.0f, BS * 1.5f, 0.0f), BS); } v3f LocalPlayer::getEyeOffset() const { - float eye_height = camera_barely_in_ceiling ? - m_eye_height - 0.125f : m_eye_height; - return v3f(0, BS * eye_height, 0); + float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height; + return v3f(0.0f, BS * eye_height, 0.0f); +} + +bool LocalPlayer::isDead() const +{ + FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized"); + return !getCAO()->isImmortal() && hp == 0; } // 3D acceleration void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H, - const f32 max_increase_V, const bool use_pitch) + const f32 max_increase_V, const bool use_pitch) { const f32 yaw = getYaw(); const f32 pitch = getPitch(); @@ -752,18 +725,18 @@ void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H, flat_speed.rotateYZBy(-pitch); v3f d_wanted = target_speed - flat_speed; - v3f d = v3f(0,0,0); + v3f d; // Then compare the horizontal and vertical components with the wanted speed - if (max_increase_H > 0) { - v3f d_wanted_H = d_wanted * v3f(1,0,1); + if (max_increase_H > 0.0f) { + v3f d_wanted_H = d_wanted * v3f(1.0f, 0.0f, 1.0f); if (d_wanted_H.getLength() > max_increase_H) d += d_wanted_H.normalize() * max_increase_H; else d += d_wanted_H; } - if (max_increase_V > 0) { + if (max_increase_V > 0.0f) { f32 d_wanted_V = d_wanted.Y; if (d_wanted_V > max_increase_V) d.Y += max_increase_V; @@ -783,7 +756,7 @@ void LocalPlayer::accelerate(const v3f &target_speed, const f32 max_increase_H, // Temporary option for old move code void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, - std::vector<CollisionInfo> *collision_info) + std::vector<CollisionInfo> *collision_info) { Map *map = &env->getMap(); const NodeDefManager *nodemgr = m_client->ndef(); @@ -794,6 +767,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, if (isAttached) { setPosition(overridePosition); m_sneak_node_exists = false; + added_velocity = v3f(0.0f); return; } @@ -807,9 +781,13 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, position += m_speed * dtime; setPosition(position); m_sneak_node_exists = false; + added_velocity = v3f(0.0f); return; } + m_speed += added_velocity; + added_velocity = v3f(0.0f); + /* Collision detection */ @@ -822,8 +800,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, */ if (in_liquid) { // If in liquid, the threshold of coming out is at higher y - pp = floatToInt(position + v3f(0, BS * 0.1, 0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f, BS * 0.1f, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; @@ -832,8 +810,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, } } else { // If not in liquid, the threshold of going in is at lower y - pp = floatToInt(position + v3f(0, BS * 0.5, 0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f, BS * 0.5f, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) { in_liquid = nodemgr->get(node.getContent()).isLiquid(); liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity; @@ -845,8 +823,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, /* Check if player is in liquid (the stable value) */ - pp = floatToInt(position + v3f(0, 0, 0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f), BS); + node = map->getNode(pp, &is_valid_position); if (is_valid_position) in_liquid_stable = nodemgr->get(node.getContent()).isLiquid(); else @@ -855,17 +833,17 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, /* Check if player is climbing */ - pp = floatToInt(position + v3f(0, 0.5 * BS, 0), BS); - v3s16 pp2 = floatToInt(position + v3f(0, -0.2 * BS, 0), BS); - node = map->getNodeNoEx(pp, &is_valid_position); + pp = floatToInt(position + v3f(0.0f, 0.5f * BS, 0.0f), BS); + v3s16 pp2 = floatToInt(position + v3f(0.0f, -0.2f * BS, 0.0f), BS); + node = map->getNode(pp, &is_valid_position); bool is_valid_position2; - MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2); + MapNode node2 = map->getNode(pp2, &is_valid_position2); if (!(is_valid_position && is_valid_position2)) is_climbing = false; else is_climbing = (nodemgr->get(node.getContent()).climbable || - nodemgr->get(node2.getContent()).climbable) && !free_move; + nodemgr->get(node2.getContent()).climbable) && !free_move; /* Collision uncertainty radius @@ -873,11 +851,11 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, */ //f32 d = pos_max_d * 1.1; // A fairly large value in here makes moving smoother - f32 d = 0.15 * BS; + f32 d = 0.15f * BS; // This should always apply, otherwise there are glitches sanity_check(d > pos_max_d); // Maximum distance over border for sneaking - f32 sneak_max = BS * 0.4; + f32 sneak_max = BS * 0.4f; /* If sneaking, keep in range from the last walked node and don't @@ -886,14 +864,14 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, if (control.sneak && m_sneak_node_exists && !(fly_allowed && player_settings.free_move) && !in_liquid && physics_override_sneak) { - f32 maxd = 0.5 * BS + sneak_max; + f32 maxd = 0.5f * BS + sneak_max; v3f lwn_f = intToFloat(m_sneak_node, BS); position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd); position.Z = rangelim(position.Z, lwn_f.Z - maxd, lwn_f.Z + maxd); if (!is_climbing) { // Move up if necessary - f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax; + f32 new_y = (lwn_f.Y - 0.5f * BS) + m_sneak_node_bb_ymax; if (position.Y < new_y) position.Y = new_y; /* @@ -901,15 +879,15 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, sneaking over the edges of current sneaking_node. TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y. */ - if (m_speed.Y < 0) - m_speed.Y = 0; + if (m_speed.Y < 0.0f) + m_speed.Y = 0.0f; } } - // this shouldn't be hardcoded but transmitted from server - float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2); + // TODO: This shouldn't be hardcoded but decided by the server + float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f); - v3f accel_f = v3f(0, 0, 0); + v3f accel_f; const v3f initial_position = position; const v3f initial_speed = m_speed; @@ -917,6 +895,12 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); + // Positition was slightly changed; update standing node pos + if (touching_ground) + m_standing_node = floatToInt(m_position - v3f(0.0f, 0.1f * BS, 0.0f), BS); + else + m_standing_node = floatToInt(m_position, BS); + /* If the player's feet touch the topside of any node, this is set to true. @@ -926,35 +910,35 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, bool touching_ground_was = touching_ground; touching_ground = result.touching_ground; - //bool standing_on_unloaded = result.standing_on_unloaded; + //bool standing_on_unloaded = result.standing_on_unloaded; /* Check the nodes under the player to see from which node the - player is sneaking from, if any. If the node from under + player is sneaking from, if any. If the node from under the player has been removed, the player falls. */ - f32 position_y_mod = 0.05 * BS; - if (m_sneak_node_bb_ymax > 0) + f32 position_y_mod = 0.05f * BS; + if (m_sneak_node_bb_ymax > 0.0f) position_y_mod = m_sneak_node_bb_ymax - position_y_mod; - v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS); + v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS); if (m_sneak_node_exists && - nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" && + nodemgr->get(map->getNode(m_old_node_below)).name == "air" && m_old_node_below_type != "air") { // Old node appears to have been removed; that is, // it wasn't air before but now it is m_need_to_get_new_sneak_node = false; m_sneak_node_exists = false; - } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") { + } else if (nodemgr->get(map->getNode(current_node)).name != "air") { // We are on something, so make sure to recalculate the sneak // node. m_need_to_get_new_sneak_node = true; } if (m_need_to_get_new_sneak_node && physics_override_sneak) { - m_sneak_node_bb_ymax = 0; - v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS); + m_sneak_node_bb_ymax = 0.0f; + v3s16 pos_i_bottom = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS); v2f player_p2df(position.X, position.Z); - f32 min_distance_f = 100000.0 * BS; + f32 min_distance_f = 100000.0f * BS; // If already seeking from some node, compare to it. v3s16 new_sneak_node = m_sneak_node; for (s16 x= -1; x <= 1; x++) @@ -964,24 +948,24 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, v2f node_p2df(pf.X, pf.Z); f32 distance_f = player_p2df.getDistanceFrom(node_p2df); f32 max_axis_distance_f = MYMAX( - std::fabs(player_p2df.X - node_p2df.X), - std::fabs(player_p2df.Y - node_p2df.Y)); + std::fabs(player_p2df.X - node_p2df.X), + std::fabs(player_p2df.Y - node_p2df.Y)); if (distance_f > min_distance_f || - max_axis_distance_f > 0.5 * BS + sneak_max + 0.1 * BS) + max_axis_distance_f > 0.5f * BS + sneak_max + 0.1f * BS) continue; // The node to be sneaked on has to be walkable - node = map->getNodeNoEx(p, &is_valid_position); + node = map->getNode(p, &is_valid_position); if (!is_valid_position || !nodemgr->get(node).walkable) continue; // And the node above it has to be nonwalkable - node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position); + node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable) continue; // If not 'sneak_glitch' the node 2 nodes above it has to be nonwalkable if (!physics_override_sneak_glitch) { - node =map->getNodeNoEx(p + v3s16(0, 2, 0), &is_valid_position); + node = map->getNode(p + v3s16(0, 2, 0), &is_valid_position); if (!is_valid_position || nodemgr->get(node).walkable) continue; } @@ -990,14 +974,14 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, new_sneak_node = p; } - bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9); + bool sneak_node_found = (min_distance_f < 100000.0f * BS * 0.9f); m_sneak_node = new_sneak_node; m_sneak_node_exists = sneak_node_found; if (sneak_node_found) { - f32 cb_max = 0; - MapNode n = map->getNodeNoEx(m_sneak_node); + f32 cb_max = 0.0f; + MapNode n = map->getNode(m_sneak_node); std::vector<aabb3f> nodeboxes; n.getCollisionBoxes(nodemgr, &nodeboxes); for (const auto &box : nodeboxes) { @@ -1025,7 +1009,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, /* Report collisions */ - // Dont report if flying + // Don't report if flying if (collision_info && !(player_settings.free_move && fly_allowed)) { for (const auto &info : result.collisions) { collision_info->push_back(info); @@ -1035,13 +1019,13 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, if (!result.standing_on_object && !touching_ground_was && touching_ground) { m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_REGAIN_GROUND)); // Set camera impact value to be used for view bobbing - camera_impact = getSpeed().Y * -1; + camera_impact = getSpeed().Y * -1.0f; } { camera_barely_in_ceiling = false; v3s16 camera_np = floatToInt(getEyePosition(), BS); - MapNode n = map->getNodeNoEx(camera_np); + MapNode n = map->getNode(camera_np); if (n.getContent() != CONTENT_IGNORE) { if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2) camera_barely_in_ceiling = true; @@ -1051,24 +1035,25 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, /* Update the node last under the player */ - m_old_node_below = floatToInt(position - v3f(0, BS / 2, 0), BS); - m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name; + m_old_node_below = floatToInt(position - v3f(0.0f, BS / 2.0f, 0.0f), BS); + m_old_node_below_type = nodemgr->get(map->getNode(m_old_node_below)).name; /* Check properties of the node on which the player is standing */ - const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos())); + const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos())); + // Determine if jumping is possible - m_can_jump = touching_ground && !in_liquid; - if (itemgroup_get(f.groups, "disable_jump")) - m_can_jump = false; + m_disable_jump = itemgroup_get(f.groups, "disable_jump"); + m_can_jump = touching_ground && !m_disable_jump; + // Jump key pressed while jumping off from a bouncy block if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") && - m_speed.Y >= -0.5 * BS) { + m_speed.Y >= -0.5f * BS) { float jumpspeed = movement_speed_jump * physics_override_jump; - if (m_speed.Y > 1) { + if (m_speed.Y > 1.0f) { // Reduce boost when speed already is high - m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 )); + m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f)); } else { m_speed.Y += jumpspeed; } @@ -1085,24 +1070,23 @@ float LocalPlayer::getSlipFactor(Environment *env, const v3f &speedH) // Slip on slippery nodes const NodeDefManager *nodemgr = env->getGameDef()->ndef(); Map *map = &env->getMap(); - const ContentFeatures &f = nodemgr->get(map->getNodeNoEx( - getStandingNodePos())); + const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos())); int slippery = 0; if (f.walkable) slippery = itemgroup_get(f.groups, "slippery"); if (slippery >= 1) { - if (speedH == v3f(0.0f)) { - slippery = slippery * 2; - } + if (speedH == v3f(0.0f)) + slippery *= 2; + return core::clamp(1.0f / (slippery + 1), 0.001f, 1.0f); } return 1.0f; } void LocalPlayer::handleAutojump(f32 dtime, Environment *env, - const collisionMoveResult &result, const v3f &initial_position, - const v3f &initial_speed, f32 pos_max_d) + const collisionMoveResult &result, const v3f &initial_position, + const v3f &initial_speed, f32 pos_max_d) { PlayerSettings &player_settings = getPlayerSettings(); if (!player_settings.autojump) @@ -1111,11 +1095,13 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, if (m_autojump) return; - bool control_forward = control.up || player_settings.continuous_forward || - (!control.up && !control.down && - control.forw_move_joystick_axis < -0.05); + bool control_forward = control.up || + (!control.up && !control.down && + control.forw_move_joystick_axis < -0.05f); + bool could_autojump = - m_can_jump && !control.jump && !control.sneak && control_forward; + m_can_jump && !control.jump && !control.sneak && control_forward; + if (!could_autojump) return; @@ -1139,9 +1125,9 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, v3s16 ceilpos_max = floatToInt(headpos_max, BS) + v3s16(0, 1, 0); const NodeDefManager *ndef = env->getGameDef()->ndef(); bool is_position_valid; - for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; z++) { - for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; x++) { - MapNode n = env->getMap().getNodeNoEx(v3s16(x, ceilpos_max.Y, z), &is_position_valid); + for (s16 z = ceilpos_min.Z; z <= ceilpos_max.Z; ++z) { + for (s16 x = ceilpos_min.X; x <= ceilpos_max.X; ++x) { + MapNode n = env->getMap().getNode(v3s16(x, ceilpos_max.Y, z), &is_position_valid); if (!is_position_valid) break; // won't collide with the void outside @@ -1159,8 +1145,7 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, // try at peak of jump, zero step height collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d, - m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, - v3f(0, 0, 0)); + m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f)); // see if we can get a little bit farther horizontally if we had // jumped diff --git a/src/client/localplayer.h b/src/client/localplayer.h index b1fc1fbc8..45dc6776e 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -100,7 +100,7 @@ public: bool makes_footstep_sound = true; int last_animation = NO_ANIM; - float last_animation_speed; + float last_animation_speed = 0.0f; std::string hotbar_image = ""; std::string hotbar_selected_image = ""; @@ -149,15 +149,22 @@ public: bool getAutojump() const { return m_autojump; } + bool isDead() const; + + inline void addVelocity(const v3f &vel) + { + added_velocity += vel; + } + private: void accelerate(const v3f &target_speed, const f32 max_increase_H, - const f32 max_increase_V, const bool use_pitch); + const f32 max_increase_V, const bool use_pitch); bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max); float getSlipFactor(Environment *env, const v3f &speedH); void handleAutojump(f32 dtime, Environment *env, - const collisionMoveResult &result, - const v3f &position_before_move, const v3f &speed_before_move, - f32 pos_max_d); + const collisionMoveResult &result, + const v3f &position_before_move, const v3f &speed_before_move, + f32 pos_max_d); v3f m_position; v3s16 m_standing_node; @@ -183,17 +190,21 @@ private: // ***** End of variables for temporary option ***** bool m_can_jump = false; + bool m_disable_jump = false; u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; f32 m_yaw = 0.0f; f32 m_pitch = 0.0f; bool camera_barely_in_ceiling = false; aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f, - BS * 1.75f, BS * 0.30f); + BS * 1.75f, BS * 0.30f); float m_eye_height = 1.625f; float m_zoom_fov = 0.0f; bool m_autojump = false; float m_autojump_time = 0.0f; + v3f added_velocity = v3f(0.0f); // cleared on each move() + // TODO: Rename to adhere to convention: added_velocity --> m_added_velocity + GenericCAO *m_cao = nullptr; Client *m_client; }; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 6b5ba9f9d..2bfaa7a4f 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -942,10 +942,7 @@ static void updateFastFaceRow( makeFastFace(tile, lights[0], lights[1], lights[2], lights[3], pf, sp, face_dir_corrected, scale, dest); - - g_profiler->avg("Meshgen: faces drawn by tiling", 0); - for (int i = 1; i < continuous_tiles_count; i++) - g_profiler->avg("Meshgen: faces drawn by tiling", 1); + g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count); } continuous_tiles_count = 1; diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 7fc7531f2..4d73ead8a 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "log.h" #include "irrMap.h" +#include <cmath> #include <iostream> #include <IAnimatedMesh.h> #include <SAnimatedMesh.h> @@ -202,6 +203,20 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) setMeshBufferColor(mesh->getMeshBuffer(j), color); } +template <typename F> +static void applyToMesh(scene::IMesh *mesh, const F &fn) +{ + u16 mc = mesh->getMeshBufferCount(); + for (u16 j = 0; j < mc; j++) { + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + char *vertices = reinterpret_cast<char *>(buf->getVertices()); + for (u32 i = 0; i < vertex_count; i++) + fn(reinterpret_cast<video::S3DVertex *>(vertices + i * stride)); + } +} + void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor) { const u32 stride = getVertexPitchFromType(buf->getVertexType()); @@ -222,28 +237,20 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh, const video::SColor &colorY, const video::SColor &colorZ) { - if (mesh == NULL) + if (!mesh) return; - - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) { - video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); - f32 x = fabs(vertex->Normal.X); - f32 y = fabs(vertex->Normal.Y); - f32 z = fabs(vertex->Normal.Z); - if (x >= y && x >= z) - vertex->Color = colorX; - else if (y >= z) - vertex->Color = colorY; - else - vertex->Color = colorZ; - } - } + auto colorizator = [=] (video::S3DVertex *vertex) { + f32 x = fabs(vertex->Normal.X); + f32 y = fabs(vertex->Normal.Y); + f32 z = fabs(vertex->Normal.Z); + if (x >= y && x >= z) + vertex->Color = colorX; + else if (y >= z) + vertex->Color = colorY; + else + vertex->Color = colorZ; + }; + applyToMesh(mesh, colorizator); } void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal, @@ -251,132 +258,58 @@ void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal, { if (!mesh) return; + auto colorizator = [normal, color] (video::S3DVertex *vertex) { + if (vertex->Normal == normal) + vertex->Color = color; + }; + applyToMesh(mesh, colorizator); +} - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) { - video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); - if (normal == vertex->Normal) { - vertex->Color = color; - } - } - } +template <float v3f::*U, float v3f::*V> +static void rotateMesh(scene::IMesh *mesh, float degrees) +{ + degrees *= M_PI / 180.0f; + float c = std::cos(degrees); + float s = std::sin(degrees); + auto rotator = [c, s] (video::S3DVertex *vertex) { + float u = vertex->Pos.*U; + float v = vertex->Pos.*V; + vertex->Pos.*U = c * u - s * v; + vertex->Pos.*V = s * u + c * v; + }; + applyToMesh(mesh, rotator); } void rotateMeshXYby(scene::IMesh *mesh, f64 degrees) { - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXYBy(degrees); - } + rotateMesh<&v3f::X, &v3f::Y>(mesh, degrees); } void rotateMeshXZby(scene::IMesh *mesh, f64 degrees) { - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXZBy(degrees); - } + rotateMesh<&v3f::X, &v3f::Z>(mesh, degrees); } void rotateMeshYZby(scene::IMesh *mesh, f64 degrees) { - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateYZBy(degrees); - } + rotateMesh<&v3f::Y, &v3f::Z>(mesh, degrees); } void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir) { int axisdir = facedir >> 2; facedir &= 0x03; - - u16 mc = mesh->getMeshBufferCount(); - for (u16 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) { - video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); - switch (axisdir) { - case 0: - if (facedir == 1) - vertex->Pos.rotateXZBy(-90); - else if (facedir == 2) - vertex->Pos.rotateXZBy(180); - else if (facedir == 3) - vertex->Pos.rotateXZBy(90); - break; - case 1: // z+ - vertex->Pos.rotateYZBy(90); - if (facedir == 1) - vertex->Pos.rotateXYBy(90); - else if (facedir == 2) - vertex->Pos.rotateXYBy(180); - else if (facedir == 3) - vertex->Pos.rotateXYBy(-90); - break; - case 2: //z- - vertex->Pos.rotateYZBy(-90); - if (facedir == 1) - vertex->Pos.rotateXYBy(-90); - else if (facedir == 2) - vertex->Pos.rotateXYBy(180); - else if (facedir == 3) - vertex->Pos.rotateXYBy(90); - break; - case 3: //x+ - vertex->Pos.rotateXYBy(-90); - if (facedir == 1) - vertex->Pos.rotateYZBy(90); - else if (facedir == 2) - vertex->Pos.rotateYZBy(180); - else if (facedir == 3) - vertex->Pos.rotateYZBy(-90); - break; - case 4: //x- - vertex->Pos.rotateXYBy(90); - if (facedir == 1) - vertex->Pos.rotateYZBy(-90); - else if (facedir == 2) - vertex->Pos.rotateYZBy(180); - else if (facedir == 3) - vertex->Pos.rotateYZBy(90); - break; - case 5: - vertex->Pos.rotateXYBy(-180); - if (facedir == 1) - vertex->Pos.rotateXZBy(90); - else if (facedir == 2) - vertex->Pos.rotateXZBy(180); - else if (facedir == 3) - vertex->Pos.rotateXZBy(-90); - break; - default: - break; - } - } + switch (facedir) { + case 1: rotateMeshXZby(mesh, -90); break; + case 2: rotateMeshXZby(mesh, 180); break; + case 3: rotateMeshXZby(mesh, 90); break; + } + switch (axisdir) { + case 1: rotateMeshYZby(mesh, 90); break; // z+ + case 2: rotateMeshYZby(mesh, -90); break; // z- + case 3: rotateMeshXYby(mesh, -90); break; // x+ + case 4: rotateMeshXYby(mesh, 90); break; // x- + case 5: rotateMeshXYby(mesh, -180); break; } } @@ -410,8 +343,8 @@ scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer) video::S3DVertex2TCoords *v = (video::S3DVertex2TCoords *) mesh_buffer->getVertices(); u16 *indices = mesh_buffer->getIndices(); - scene::SMeshBufferTangents *cloned_buffer = - new scene::SMeshBufferTangents(); + scene::SMeshBufferLightMap *cloned_buffer = + new scene::SMeshBufferLightMap(); cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices, mesh_buffer->getIndexCount()); return cloned_buffer; diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index be4bcc1f4..53b980eeb 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -98,7 +98,7 @@ void MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool &cache_hit_counter); cached_blocks.push_back(cached_block); } - g_profiler->avg("MeshUpdateQueue MapBlock cache hit %", + g_profiler->avg("MeshUpdateQueue: MapBlocks from cache [%]", 100.0f * cache_hit_counter / cached_blocks.size()); /* @@ -162,39 +162,36 @@ QueuedMeshUpdate *MeshUpdateQueue::pop() CachedMapBlockData* MeshUpdateQueue::cacheBlock(Map *map, v3s16 p, UpdateMode mode, size_t *cache_hit_counter) { + CachedMapBlockData *cached_block = nullptr; std::map<v3s16, CachedMapBlockData*>::iterator it = m_cache.find(p); + if (it != m_cache.end()) { - // Already in cache - CachedMapBlockData *cached_block = it->second; + cached_block = it->second; + if (mode == SKIP_UPDATE_IF_ALREADY_CACHED) { if (cache_hit_counter) (*cache_hit_counter)++; return cached_block; } - MapBlock *b = map->getBlockNoCreateNoEx(p); - if (b) { - if (cached_block->data == NULL) - cached_block->data = - new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE]; - memcpy(cached_block->data, b->getData(), - MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode)); - } else { - delete[] cached_block->data; - cached_block->data = NULL; - } - return cached_block; } - // Not yet in cache - CachedMapBlockData *cached_block = new CachedMapBlockData(); - m_cache[p] = cached_block; + if (!cached_block) { + // Not yet in cache + cached_block = new CachedMapBlockData(); + m_cache[p] = cached_block; + } + MapBlock *b = map->getBlockNoCreateNoEx(p); if (b) { - cached_block->data = - new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE]; + if (!cached_block->data) + cached_block->data = + new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE]; memcpy(cached_block->data, b->getData(), MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode)); + } else { + delete[] cached_block->data; + cached_block->data = nullptr; } return cached_block; } @@ -292,7 +289,7 @@ void MeshUpdateThread::doUpdate() while ((q = m_queue_in.pop())) { if (m_generation_interval) sleep_ms(m_generation_interval); - ScopeProfiler sp(g_profiler, "Client: Mesh making"); + ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)"); MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset); diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 4d83c088a..68770ec19 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -569,7 +569,7 @@ void Minimap::updateActiveMarkers() m_active_markers.clear(); for (Nametag *nametag : nametags) { - v3s16 pos = floatToInt(nametag->parent_node->getPosition() + + 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, diff --git a/src/client/particles.cpp b/src/client/particles.cpp index ebd52f0f0..a0e4e54eb 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <cmath> #include "client.h" #include "collision.h" +#include "client/content_cao.h" #include "client/clientevent.h" #include "client/renderingengine.h" #include "util/numeric.h" @@ -38,9 +39,10 @@ with this program; if not, write to the Free Software Foundation, Inc., v3f random_v3f(v3f min, v3f max) { - return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X, - rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y, - rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z); + return v3f( + rand() / (float)RAND_MAX * (max.X - min.X) + min.X, + rand() / (float)RAND_MAX * (max.Y - min.Y) + min.Y, + rand() / (float)RAND_MAX * (max.Z - min.Z) + min.Z); } Particle::Particle( @@ -99,8 +101,13 @@ Particle::Particle( m_glow = glow; // Irrlicht stuff - m_collisionbox = aabb3f - (-size/2,-size/2,-size/2,size/2,size/2,size/2); + m_collisionbox = aabb3f( + -size / 2, + -size / 2, + -size / 2, + size / 2, + size / 2, + size / 2); this->setAutomaticCulling(scene::EAC_OFF); // Init lighting @@ -120,7 +127,7 @@ void Particle::OnRegisterSceneNode() void Particle::render() { - video::IVideoDriver* driver = SceneManager->getVideoDriver(); + video::IVideoDriver *driver = SceneManager->getVideoDriver(); driver->setMaterial(m_material); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); @@ -181,7 +188,7 @@ void Particle::updateLight() floor(m_pos.Y+0.5), floor(m_pos.Z+0.5) ); - MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok); + MapNode n = m_env->getClientMap().getNode(p, &pos_ok); if (pos_ok) light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef()); else @@ -291,24 +298,29 @@ ParticleSpawner::ParticleSpawner( m_animation = anim; m_glow = glow; - for (u16 i = 0; i<=m_amount; i++) + for (u16 i = 0; i <= m_amount; i++) { - float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime; + float spawntime = (float)rand() / (float)RAND_MAX * m_spawntime; m_spawntimes.push_back(spawntime); } } void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, - bool is_attached, const v3f &attached_pos, float attached_yaw) + const core::matrix4 *attached_absolute_pos_rot_matrix) { v3f ppos = m_player->getPosition() / BS; v3f pos = random_v3f(m_minpos, m_maxpos); // Need to apply this first or the following check // will be wrong for attached spawners - if (is_attached) { - pos.rotateXZBy(attached_yaw); - pos += attached_pos; + if (attached_absolute_pos_rot_matrix) { + pos *= BS; + attached_absolute_pos_rot_matrix->transformVect(pos); + pos /= BS; + v3s16 camera_offset = m_particlemanager->m_env->getCameraOffset(); + pos.X += camera_offset.X; + pos.Y += camera_offset.Y; + pos.Z += camera_offset.Z; } if (pos.getDistanceFrom(ppos) > radius) @@ -317,18 +329,19 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, v3f vel = random_v3f(m_minvel, m_maxvel); v3f acc = random_v3f(m_minacc, m_maxacc); - if (is_attached) { - // Apply attachment yaw - vel.rotateXZBy(attached_yaw); - acc.rotateXZBy(attached_yaw); + if (attached_absolute_pos_rot_matrix) { + // Apply attachment rotation + attached_absolute_pos_rot_matrix->rotateVect(vel); + attached_absolute_pos_rot_matrix->rotateVect(acc); } float exptime = rand() / (float)RAND_MAX - * (m_maxexptime - m_minexptime) - + m_minexptime; + * (m_maxexptime - m_minexptime) + + m_minexptime; + float size = rand() / (float)RAND_MAX - * (m_maxsize - m_minsize) - + m_minsize; + * (m_maxsize - m_minsize) + + m_minsize; m_particlemanager->addParticle(new Particle( m_gamedef, @@ -351,7 +364,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, )); } -void ParticleSpawner::step(float dtime, ClientEnvironment* env) +void ParticleSpawner::step(float dtime, ClientEnvironment *env) { m_time += dtime; @@ -359,14 +372,10 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE; bool unloaded = false; - bool is_attached = false; - v3f attached_pos = v3f(0,0,0); - float attached_yaw = 0; - if (m_attached_id != 0) { - if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) { - attached_pos = attached->getPosition() / BS; - attached_yaw = attached->getYaw(); - is_attached = true; + const core::matrix4 *attached_absolute_pos_rot_matrix = nullptr; + if (m_attached_id) { + if (GenericCAO *attached = dynamic_cast<GenericCAO *>(env->getActiveObject(m_attached_id))) { + attached_absolute_pos_rot_matrix = &attached->getAbsolutePosRotMatrix(); } else { unloaded = true; } @@ -377,12 +386,12 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) for (std::vector<float>::iterator i = m_spawntimes.begin(); i != m_spawntimes.end();) { if ((*i) <= m_time && m_amount > 0) { - m_amount--; + --m_amount; // Pretend to, but don't actually spawn a particle if it is // attached to an unloaded object or distant from player. if (!unloaded) - spawnParticle(env, radius, is_attached, attached_pos, attached_yaw); + spawnParticle(env, radius, attached_absolute_pos_rot_matrix); i = m_spawntimes.erase(i); } else { @@ -398,13 +407,13 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) for (int i = 0; i <= m_amount; i++) { if (rand() / (float)RAND_MAX < dtime) - spawnParticle(env, radius, is_attached, attached_pos, attached_yaw); + spawnParticle(env, radius, attached_absolute_pos_rot_matrix); } } } -ParticleManager::ParticleManager(ClientEnvironment* env) : +ParticleManager::ParticleManager(ClientEnvironment *env) : m_env(env) {} @@ -419,7 +428,7 @@ void ParticleManager::step(float dtime) stepSpawners (dtime); } -void ParticleManager::stepSpawners (float dtime) +void ParticleManager::stepSpawners(float dtime) { MutexAutoLock lock(m_spawner_list_lock); for (auto i = m_particle_spawners.begin(); i != m_particle_spawners.end();) { @@ -433,7 +442,7 @@ void ParticleManager::stepSpawners (float dtime) } } -void ParticleManager::stepParticles (float dtime) +void ParticleManager::stepParticles(float dtime) { MutexAutoLock lock(m_particle_list_lock); for (auto i = m_particles.begin(); i != m_particles.end();) { @@ -448,7 +457,7 @@ void ParticleManager::stepParticles (float dtime) } } -void ParticleManager::clearAll () +void ParticleManager::clearAll() { MutexAutoLock lock(m_spawner_list_lock); MutexAutoLock lock2(m_particle_list_lock); @@ -457,9 +466,7 @@ void ParticleManager::clearAll () m_particle_spawners.erase(i++); } - for(std::vector<Particle*>::iterator i = - m_particles.begin(); - i != m_particles.end();) + for(auto i = m_particles.begin(); i != m_particles.end();) { (*i)->remove(); delete *i; @@ -568,7 +575,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, // The final burst of particles when a node is finally dug, *not* particles // spawned during the digging of a node. -void ParticleManager::addDiggingParticles(IGameDef* gamedef, +void ParticleManager::addDiggingParticles(IGameDef *gamedef, LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f) { // No particles for "airlike" nodes @@ -583,7 +590,7 @@ void ParticleManager::addDiggingParticles(IGameDef* gamedef, // During the digging of a node particles are spawned individually by this // function, called from Game::handleDigging() in game.cpp. -void ParticleManager::addNodeParticle(IGameDef* gamedef, +void ParticleManager::addNodeParticle(IGameDef *gamedef, LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f) { // No particles for "airlike" nodes @@ -635,7 +642,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, else n.getColor(f, &color); - Particle* toadd = new Particle( + Particle *toadd = new Particle( gamedef, player, m_env, @@ -658,7 +665,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, addParticle(toadd); } -void ParticleManager::addParticle(Particle* toadd) +void ParticleManager::addParticle(Particle *toadd) { MutexAutoLock lock(m_particle_list_lock); m_particles.push_back(toadd); diff --git a/src/client/particles.h b/src/client/particles.h index 353743372..e7b8cbe24 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -144,8 +144,7 @@ public: private: void spawnParticle(ClientEnvironment *env, float radius, - bool is_attached, const v3f &attached_pos, - float attached_yaw); + const core::matrix4 *attached_absolute_pos_rot_matrix); ParticleManager *m_particlemanager; float m_time; diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index 8c70b36c6..bf5aa6c2c 100644 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@ -86,7 +86,7 @@ void RenderingCore::drawHUD() if (show_hud) { if (draw_crosshair) hud->drawCrosshair(); - hud->drawHotbar(client->getPlayerItem()); + hud->drawHotbar(client->getEnv().getLocalPlayer()->getWieldIndex()); hud->drawLuaElements(camera->getOffset()); camera->drawNametags(); if (mapper && show_minimap) diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index cc17c98c3..a130a14eb 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -35,7 +35,7 @@ RenderingCorePlain::RenderingCorePlain( void RenderingCorePlain::initTextures() { - if (!scale) + if (scale <= 1) return; v2u32 size{scaledown(scale, screensize.X), scaledown(scale, screensize.Y)}; lowres = driver->addRenderTargetTexture( @@ -44,21 +44,21 @@ void RenderingCorePlain::initTextures() void RenderingCorePlain::clearTextures() { - if (!scale) + if (scale <= 1) return; driver->removeTexture(lowres); } void RenderingCorePlain::beforeDraw() { - if (!scale) + if (scale <= 1) return; driver->setRenderTarget(lowres, true, true, skycolor); } void RenderingCorePlain::upscale() { - if (!scale) + if (scale <= 1) return; driver->setRenderTarget(0, true, true); v2u32 size{scaledown(scale, screensize.X), scaledown(scale, screensize.Y)}; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index b18e91f6e..6e6509eeb 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "render/factory.h" #include "inputhandler.h" #include "gettext.h" +#include "../gui/guiSkin.h" #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__) && \ !defined(SERVER) && !defined(__HAIKU__) @@ -44,14 +45,38 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> + #endif -#ifdef __ANDROID__ +#if ENABLE_GLES #include "filesys.h" #endif RenderingEngine *RenderingEngine::s_singleton = nullptr; + +static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment, + gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) +{ + gui::GUISkin *skin = new gui::GUISkin(type, driver); + + gui::IGUIFont *builtinfont = environment->getBuiltInFont(); + gui::IGUIFontBitmap *bitfont = nullptr; + if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP) + bitfont = (gui::IGUIFontBitmap*)builtinfont; + + gui::IGUISpriteBank *bank = 0; + skin->setFont(builtinfont); + + if (bitfont) + bank = bitfont->getSpriteBank(); + + skin->setSpriteBank(bank); + + return skin; +} + + RenderingEngine::RenderingEngine(IEventReceiver *receiver) { sanity_check(!s_singleton); @@ -77,7 +102,7 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) u32 i; for (i = 0; i != drivers.size(); i++) { if (!strcasecmp(driverstring.c_str(), - RenderingEngine::getVideoDriverName(drivers[i]))) { + RenderingEngine::getVideoDriverName(drivers[i]))) { driverType = drivers[i]; break; } @@ -106,12 +131,22 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM + "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str(); // clang-format on +#elif ENABLE_GLES + // there is no standardized path for these on desktop + std::string rel_path = std::string("client") + DIR_DELIM + + "shaders" + DIR_DELIM + "Irrlicht"; + params.OGLES2ShaderPath = (porting::path_share + DIR_DELIM + rel_path + DIR_DELIM).c_str(); #endif m_device = createDeviceEx(params); driver = m_device->getVideoDriver(); s_singleton = this; + + auto skin = createSkin(m_device->getGUIEnvironment(), + gui::EGST_WINDOWS_METALLIC, driver); + m_device->getGUIEnvironment()->setSkin(skin); + skin->drop(); } RenderingEngine::~RenderingEngine() @@ -193,7 +228,7 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) // sort here that would call the correct toplevel setup methods for // the environment Minetest is running in but for now not deviating // from the original pattern. - + /* Setting Xorg properties for the top level window */ setupTopLevelXorgWindow(name); /* Done with Xorg properties */ @@ -211,7 +246,7 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) /* Done with general properties */ // FIXME: setWindowIcon returns a bool result but it is unused. - // For now continue to return this result. + // For now continue to return this result. return result; } @@ -223,7 +258,7 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) Display *x11_dpl = reinterpret_cast<Display *>(exposedData.OpenGLLinux.X11Display); if (x11_dpl == NULL) { warningstream << "Client: Could not find X11 Display in ExposedVideoData" - << std::endl; + << std::endl; return; } @@ -244,30 +279,30 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) // FIXME: In the future WMNormalHints should be set ... e.g see the // gtk/gdk code (gdk/x11/gdksurface-x11.c) for the setup_top_level - // method. But for now (as it would require some significant changes) - // leave the code as is. - + // method. But for now (as it would require some significant changes) + // leave the code as is. + // The following is borrowed from the above gdk source for setting top // level windows. The source indicates and the Xlib docs suggest that - // this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not - // set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is + // this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not + // set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is // required by the Extended Window Manager Hints (EWMH) spec when setting // the _NET_WM_PID (see further down) but running Minetest in an env // where the window manager is on another machine from Minetest (therefore // making the PID useless) is not expected to be a problem. Further // more, using gtk/gdk as the model it would seem that not using a FQDN is // not an issue for modern Xorg window managers. - + verbosestream << "Client: Setting Xorg window manager Properties" << std::endl; XSetWMProperties (x11_dpl, x11_win, NULL, NULL, NULL, 0, NULL, NULL, NULL); - // Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID - // (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to - // force a shutdown of an application if it doesn't respond to the destroy - // window message. - + // Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID + // (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to + // force a shutdown of an application if it doesn't respond to the destroy + // window message. + verbosestream << "Client: Setting Xorg _NET_WM_PID extened window manager property" << std::endl; @@ -277,12 +312,12 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) infostream << "Client: PID is '" << static_cast<long>(pid) << "'" << std::endl; - XChangeProperty(x11_dpl, x11_win, NET_WM_PID, - XA_CARDINAL, 32, PropModeReplace, + XChangeProperty(x11_dpl, x11_win, NET_WM_PID, + XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<unsigned char *>(&pid),1); // Set the WM_CLIENT_LEADER window property here. Minetest has only one - // window and that window will always be the leader. + // window and that window will always be the leader. verbosestream << "Client: Setting Xorg WM_CLIENT_LEADER property" << std::endl; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 3b49a36ba..f36ff3d85 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -527,15 +527,18 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp switch (material_type) { case TILE_MATERIAL_OPAQUE: case TILE_MATERIAL_LIQUID_OPAQUE: + case TILE_MATERIAL_WAVING_LIQUID_OPAQUE: shaderinfo.base_material = video::EMT_SOLID; break; case TILE_MATERIAL_ALPHA: case TILE_MATERIAL_LIQUID_TRANSPARENT: + case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_BASIC: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: + case TILE_MATERIAL_WAVING_LIQUID_BASIC: shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; } @@ -631,10 +634,13 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp "TILE_MATERIAL_LIQUID_OPAQUE", "TILE_MATERIAL_WAVING_LEAVES", "TILE_MATERIAL_WAVING_PLANTS", - "TILE_MATERIAL_OPAQUE" + "TILE_MATERIAL_OPAQUE", + "TILE_MATERIAL_WAVING_LIQUID_BASIC", + "TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT", + "TILE_MATERIAL_WAVING_LIQUID_OPAQUE", }; - for (int i = 0; i < 7; i++){ + for (int i = 0; i < 10; i++){ shaders_header += "#define "; shaders_header += materialTypes[i]; shaders_header += " "; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index ff968444d..346cd0642 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/renderingengine.h" #include "settings.h" #include "camera.h" // CameraModes +#include "config.h" Sky::Sky(s32 id, ITextureSource *tsrc): @@ -44,7 +45,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc): video::SMaterial mat; mat.Lighting = false; -#ifdef __ANDROID__ +#if ENABLE_GLES mat.ZBuffer = video::ECFN_DISABLED; #else mat.ZBuffer = video::ECFN_NEVER; @@ -118,8 +119,8 @@ void Sky::render() if (!m_visible) return; - video::IVideoDriver* driver = SceneManager->getVideoDriver(); - scene::ICameraSceneNode* camera = SceneManager->getActiveCamera(); + video::IVideoDriver *driver = SceneManager->getVideoDriver(); + scene::ICameraSceneNode *camera = SceneManager->getActiveCamera(); if (!camera || !driver) return; @@ -273,7 +274,7 @@ void Sky::render() // Stars are only drawn when brighter than skycolor if (starcolor.getBlue() < m_skycolor.getBlue()) break; -#ifdef __ANDROID__ +#if ENABLE_GLES u16 indices[SKY_STAR_COUNT * 3]; video::S3DVertex vertices[SKY_STAR_COUNT * 3]; for (u32 i = 0; i < SKY_STAR_COUNT; i++) { @@ -337,7 +338,7 @@ void Sky::render() indices, SKY_STAR_COUNT, video::EVT_STANDARD, scene::EPT_QUADS, video::EIT_16BIT); #endif - } while(false); + } while (false); // Draw sunrise/sunset horizon glow texture (textures/base/pack/sunrisebg.png) { @@ -366,154 +367,12 @@ void Sky::render() // Draw sun if (wicked_time_of_day > 0.15 && wicked_time_of_day < 0.85) { - if (!m_sun_texture) { - driver->setMaterial(m_materials[1]); - float d = sunsize * 1.7; - video::SColor c = suncolor; - c.setAlpha(0.05 * 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - d = sunsize * 1.2; - c = suncolor; - c.setAlpha(0.15 * 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - d = sunsize; - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, suncolor, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, suncolor, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, suncolor, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, suncolor, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - d = sunsize * 0.7; - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, suncolor2, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, suncolor2, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, suncolor2, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, suncolor2, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } else { - driver->setMaterial(m_materials[3]); - float d = sunsize * 1.7; - video::SColor c; - if (m_sun_tonemap) - c = video::SColor (0, 0, 0, 0); - else - c = video::SColor (255, 255, 255, 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } + draw_sun(driver, sunsize, suncolor, suncolor2, wicked_time_of_day); } // Draw moon if (wicked_time_of_day < 0.3 || wicked_time_of_day > 0.7) { - if (!m_moon_texture) { - driver->setMaterial(m_materials[1]); - float d = moonsize * 1.9; - video::SColor c = mooncolor; - c.setAlpha(0.05 * 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - d = moonsize * 1.3; - c = mooncolor; - c.setAlpha(0.15 * 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - d = moonsize; - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, mooncolor, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, mooncolor, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, mooncolor, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, mooncolor, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - - float d2 = moonsize * 0.6; - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, mooncolor2, t, t); - vertices[1] = video::S3DVertex( d2,-d, -1, 0, 0, 1, mooncolor2, o, t); - vertices[2] = video::S3DVertex( d2, d2, -1, 0, 0, 1, mooncolor2, o, o); - vertices[3] = video::S3DVertex(-d, d2, -1, 0, 0, 1, mooncolor2, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } else { - driver->setMaterial(m_materials[4]); - float d = moonsize * 1.9; - video::SColor c; - if (m_moon_tonemap) - c = video::SColor (0, 0, 0, 0); - else - c = video::SColor (255, 255, 255, 255); - vertices[0] = video::S3DVertex(-d, -d, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( d, -d, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( d, d, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-d, d, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - vertex.Pos.rotateXYBy(wicked_time_of_day * 360 - 90); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } + draw_moon(driver, moonsize, mooncolor, mooncolor2, wicked_time_of_day); } // Draw far cloudy fog thing below all horizons in front of sun, moon @@ -555,8 +414,8 @@ void Sky::render() void Sky::update(float time_of_day, float time_brightness, - float direct_brightness, bool sunlight_seen, - CameraMode cam_mode, float yaw, float pitch) + float direct_brightness, bool sunlight_seen, + CameraMode cam_mode, float yaw, float pitch) { // Stabilize initial brightness and color values by flooding updates if (m_first_update) { @@ -766,3 +625,128 @@ void Sky::update(float time_of_day, float time_brightness, video::SColorf(pointcolor), m_horizon_blend() * 0.25); } } + +void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SColor &suncolor, + const video::SColor &suncolor2, float wicked_time_of_day) + /* Draw sun in the sky. + * driver: Video driver object used to draw + * sunsize: the default size of the sun + * suncolor: main sun color + * suncolor2: second sun color + * 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}; + std::array<video::S3DVertex, 4> vertices; + if (!m_sun_texture) { + driver->setMaterial(m_materials[1]); + const float sunsizes[4] = {sunsize * 1.7f, sunsize * 1.2f, sunsize, sunsize * 0.7f}; + video::SColor c1 = suncolor; + video::SColor c2 = suncolor; + c1.setAlpha(0.05 * 255); + c2.setAlpha(0.15 * 255); + const video::SColor colors[4] = {c1, c2, suncolor, suncolor2}; + 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); + } + } else { + driver->setMaterial(m_materials[3]); + float d = sunsize * 1.7; + video::SColor c; + if (m_sun_tonemap) + c = video::SColor(0, 0, 0, 0); + else + 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); + } +} + + +void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SColor &mooncolor, + const video::SColor &mooncolor2, float wicked_time_of_day) + /* + * Draw moon in the sky. + * driver: Video driver object used to draw + * moonsize: the default size of the moon + * mooncolor: main moon color + * mooncolor2: second moon color + * wicked_time_of_day: current time of day, to know where should be the moon in the sky + */ +{ + static const u16 indices[4] = {0, 1, 2, 3}; + std::array<video::S3DVertex, 4> vertices; + if (!m_moon_texture) { + driver->setMaterial(m_materials[1]); + const float moonsizes_1[4] = { + -moonsize * 1.9f, + -moonsize * 1.3f, + -moonsize, + -moonsize + }; + const float moonsizes_2[4] = { + moonsize * 1.9f, + moonsize * 1.3f, + moonsize, + moonsize * 0.6f + }; + video::SColor c1 = mooncolor; + video::SColor c2 = mooncolor; + c1.setAlpha(0.05 * 255); + c2.setAlpha(0.15 * 255); + const video::SColor colors[4] = {c1, c2, mooncolor, mooncolor2}; + 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); + } + } else { + driver->setMaterial(m_materials[4]); + float d = moonsize * 1.9; + video::SColor c; + if (m_moon_tonemap) + c = video::SColor(0, 0, 0, 0); + else + 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); + } +} + + +void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, float pos_2, const video::SColor &c) +{ + /* + * Create an array of vertices with the dimensions specified. + * pos_1, pos_2: position of the body's vertices + * c: color of the body + */ + + const f32 t = 1.0f; + const f32 o = 0.0f; + vertices[0] = video::S3DVertex(pos_1, pos_1, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex(pos_2, pos_1, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex(pos_2, pos_2, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(pos_1, pos_2, -1, 0, 0, 1, c, t, o); +} + + +void Sky::place_sky_body( + std::array<video::S3DVertex, 4> &vertices, float horizon_position, float day_position) + /* + * Place body in the sky. + * vertices: The body as a rectangle of 4 vertices + * horizon_position: turn the body around the Y axis + * day_position: turn the body around the Z axis, to place it depending of the time of the day + */ +{ + for (video::S3DVertex &vertex : vertices) { + // Body is directed to -Z (south) by default + vertex.Pos.rotateXZBy(horizon_position); + vertex.Pos.rotateXYBy(day_position); + } +} diff --git a/src/client/sky.h b/src/client/sky.h index b66a4990f..9cff20e08 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include <ISceneNode.h> +#include <array> #include "camera.h" #include "irrlichttypes_extrabloated.h" @@ -145,4 +146,13 @@ private: video::ITexture *m_moon_texture; video::ITexture *m_sun_tonemap; video::ITexture *m_moon_tonemap; + 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, + const video::SColor &mooncolor2, float wicked_time_of_day); + void draw_sky_body(std::array<video::S3DVertex, 4> &vertices, + float pos_1, float pos_2, const video::SColor &c); + void place_sky_body( + std::array<video::S3DVertex, 4> &vertices, float horizon_position, + float day_position); }; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 4911013ae..3d9e2470a 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <algorithm> #include <ICameraSceneNode.h> +#include <IrrCompileConfig.h> #include "util/string.h" #include "util/container.h" #include "util/thread.h" @@ -34,8 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "renderingengine.h" -#ifdef __ANDROID__ +#if ENABLE_GLES +#ifdef _IRR_COMPILE_WITH_OGLES1_ #include <GLES/gl.h> +#else +#include <GLES2/gl2.h> +#endif #endif /* @@ -117,9 +122,14 @@ std::string getImagePath(std::string path) Utilizes a thread-safe cache. */ -std::string getTexturePath(const std::string &filename) +std::string getTexturePath(const std::string &filename, bool *is_base_pack) { std::string fullpath; + + // This can set a wrong value on cached textures, but is irrelevant because + // is_base_pack is only passed when initializing the textures the first time + if (is_base_pack) + *is_base_pack = false; /* Check from cache */ @@ -131,7 +141,8 @@ std::string getTexturePath(const std::string &filename) Check from texture_path */ for (const auto &path : getTextureDirs()) { - std::string testpath = path + DIR_DELIM + filename; + std::string testpath = path + DIR_DELIM; + testpath.append(filename); // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); if (!fullpath.empty()) @@ -148,6 +159,8 @@ std::string getTexturePath(const std::string &filename) std::string testpath = base_path + DIR_DELIM + filename; // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); + if (is_base_pack && !fullpath.empty()) + *is_base_pack = true; } // Add to cache (also an empty result is cached) @@ -209,9 +222,11 @@ public: bool need_to_grab = true; // Try to use local texture instead if asked to - if (prefer_local){ - std::string path = getTexturePath(name); - if (!path.empty()) { + if (prefer_local) { + bool is_base_pack; + std::string path = getTexturePath(name, &is_base_pack); + // Ignore base pack + if (!path.empty() && !is_base_pack) { video::IImage *img2 = RenderingEngine::get_video_driver()-> createImageFromFile(path.c_str()); if (img2){ @@ -594,7 +609,7 @@ u32 TextureSource::generateTexture(const std::string &name) video::ITexture *tex = NULL; if (img != NULL) { -#ifdef __ANDROID__ +#if ENABLE_GLES img = Align2Npot2(img, driver); #endif // Create texture from resulting image @@ -751,7 +766,7 @@ void TextureSource::rebuildImagesAndTextures() // Recreate textures for (TextureInfo &ti : m_textureinfo_cache) { video::IImage *img = generateImage(ti.name); -#ifdef __ANDROID__ +#if ENABLE_GLES img = Align2Npot2(img, driver); #endif // Create texture from resulting image @@ -988,8 +1003,30 @@ video::IImage* TextureSource::generateImage(const std::string &name) return baseimg; } -#ifdef __ANDROID__ -#include <GLES/gl.h> +#if ENABLE_GLES + + +static inline u16 get_GL_major_version() +{ + const GLubyte *gl_version = glGetString(GL_VERSION); + return (u16) (gl_version[0] - '0'); +} + +/** + * Check if hardware requires npot2 aligned textures + * @return true if alignment NOT(!) requires, false otherwise + */ + +bool hasNPotSupport() +{ + // Only GLES2 is trusted to correctly report npot support + // Note: we cache the boolean result, the GL context will never change. + static const bool supported = get_GL_major_version() > 1 && + glGetString(GL_EXTENSIONS) && + strstr((char *)glGetString(GL_EXTENSIONS), "GL_OES_texture_npot"); + return supported; +} + /** * Check and align image to npot2 if required by hardware * @param image image to check for npot2 alignment @@ -997,53 +1034,33 @@ video::IImage* TextureSource::generateImage(const std::string &name) * @return image or copy of image aligned to npot2 */ -inline u16 get_GL_major_version() -{ - const GLubyte *gl_version = glGetString(GL_VERSION); - return (u16) (gl_version[0] - '0'); -} - video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver) { - if (image == NULL) { + if (image == NULL) return image; - } - core::dimension2d<u32> dim = image->getDimension(); - - // Only GLES2 is trusted to correctly report npot support - // Note: we cache the boolean result. GL context will never change on Android. - static const bool hasNPotSupport = get_GL_major_version() > 1 && - glGetString(GL_EXTENSIONS) && - strstr((char *)glGetString(GL_EXTENSIONS), "GL_OES_texture_npot"); - - if (hasNPotSupport) + if (hasNPotSupport()) return image; + core::dimension2d<u32> dim = image->getDimension(); unsigned int height = npot2(dim.Height); unsigned int width = npot2(dim.Width); - if ((dim.Height == height) && - (dim.Width == width)) { + if (dim.Height == height && dim.Width == width) return image; - } - if (dim.Height > height) { + if (dim.Height > height) height *= 2; - } - - if (dim.Width > width) { + if (dim.Width > width) width *= 2; - } video::IImage *targetimage = driver->createImage(video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height)); - if (targetimage != NULL) { + if (targetimage != NULL) image->copyToScaling(targetimage); - } image->drop(); return targetimage; } @@ -1077,7 +1094,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, // Stuff starting with [ are special commands if (part_of_name.empty() || part_of_name[0] != '[') { video::IImage *image = m_sourcecache.getOrLoad(part_of_name); -#ifdef __ANDROID__ +#if ENABLE_GLES image = Align2Npot2(image, driver); #endif if (image == NULL) { diff --git a/src/client/tile.h b/src/client/tile.h index 2a33dd160..533df676e 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -27,8 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <SMaterial.h> #include <memory> #include "util/numeric.h" +#include "config.h" -#if __ANDROID__ +#if ENABLE_GLES #include <IVideoDriver.h> #endif @@ -63,7 +64,7 @@ std::string getImagePath(std::string path); Utilizes a thread-safe cache. */ -std::string getTexturePath(const std::string &filename); +std::string getTexturePath(const std::string &filename, bool *is_base_pack = nullptr); void clearTextureNameCache(); @@ -133,7 +134,8 @@ public: IWritableTextureSource *createTextureSource(); -#ifdef __ANDROID__ +#if ENABLE_GLES +bool hasNPotSupport(); video::IImage * Align2Npot2(video::IImage * image, irr::video::IVideoDriver* driver); #endif @@ -144,7 +146,10 @@ enum MaterialType{ TILE_MATERIAL_LIQUID_OPAQUE, TILE_MATERIAL_WAVING_LEAVES, TILE_MATERIAL_WAVING_PLANTS, - TILE_MATERIAL_OPAQUE + TILE_MATERIAL_OPAQUE, + TILE_MATERIAL_WAVING_LIQUID_BASIC, + TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT, + TILE_MATERIAL_WAVING_LIQUID_OPAQUE, }; // Material flags @@ -208,16 +213,19 @@ struct TileLayer switch (material_type) { case TILE_MATERIAL_OPAQUE: case TILE_MATERIAL_LIQUID_OPAQUE: + case TILE_MATERIAL_WAVING_LIQUID_OPAQUE: material.MaterialType = video::EMT_SOLID; break; case TILE_MATERIAL_BASIC: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: + case TILE_MATERIAL_WAVING_LIQUID_BASIC: material.MaterialTypeParam = 0.5; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; case TILE_MATERIAL_ALPHA: case TILE_MATERIAL_LIQUID_TRANSPARENT: + case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; default: |