diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/camera.cpp | 35 | ||||
-rw-r--r-- | src/client/client.cpp | 128 | ||||
-rw-r--r-- | src/client/client.h | 32 | ||||
-rw-r--r-- | src/client/clientenvironment.cpp | 57 | ||||
-rw-r--r-- | src/client/clientevent.h | 16 | ||||
-rw-r--r-- | src/client/clientobject.h | 6 | ||||
-rw-r--r-- | src/client/content_cao.cpp | 214 | ||||
-rw-r--r-- | src/client/content_cao.h | 10 | ||||
-rw-r--r-- | src/client/content_mapblock.cpp | 43 | ||||
-rw-r--r-- | src/client/fontengine.cpp | 127 | ||||
-rw-r--r-- | src/client/fontengine.h | 79 | ||||
-rw-r--r-- | src/client/game.cpp | 111 | ||||
-rw-r--r-- | src/client/gameui.cpp | 12 | ||||
-rw-r--r-- | src/client/guiscalingfilter.cpp | 7 | ||||
-rw-r--r-- | src/client/guiscalingfilter.h | 3 | ||||
-rw-r--r-- | src/client/hud.cpp | 94 | ||||
-rw-r--r-- | src/client/hud.h | 12 | ||||
-rw-r--r-- | src/client/keycode.cpp | 3 | ||||
-rw-r--r-- | src/client/localplayer.cpp | 15 | ||||
-rw-r--r-- | src/client/localplayer.h | 7 | ||||
-rw-r--r-- | src/client/mapblock_mesh.cpp | 12 | ||||
-rw-r--r-- | src/client/shader.cpp | 8 | ||||
-rw-r--r-- | src/client/shader.h | 2 | ||||
-rw-r--r-- | src/client/sky.cpp | 695 | ||||
-rw-r--r-- | src/client/sky.h | 70 | ||||
-rw-r--r-- | src/client/tile.cpp | 22 |
26 files changed, 1254 insertions, 566 deletions
diff --git a/src/client/camera.cpp b/src/client/camera.cpp index d1e76026d..871ea709d 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "camera.h" #include "debug.h" #include "client.h" +#include "config.h" #include "map.h" #include "clientmap.h" // MapDrawControl #include "player.h" @@ -39,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CAMERA_OFFSET_STEP 200 #define WIELDMESH_OFFSET_X 55.0f #define WIELDMESH_OFFSET_Y -35.0f +#define WIELDMESH_AMPLITUDE_X 7.0f +#define WIELDMESH_AMPLITUDE_Y 10.0f Camera::Camera(MapDrawControl &draw_control, Client *client): m_draw_control(draw_control), @@ -234,7 +237,8 @@ void Camera::addArmInertia(f32 player_yaw) m_last_cam_pos.X = player_yaw; m_wieldmesh_offset.X = rangelim(m_wieldmesh_offset.X, - WIELDMESH_OFFSET_X - 7.0f, WIELDMESH_OFFSET_X + 7.0f); + WIELDMESH_OFFSET_X - (WIELDMESH_AMPLITUDE_X * 0.5f), + WIELDMESH_OFFSET_X + (WIELDMESH_AMPLITUDE_X * 0.5f)); } if (m_cam_vel.Y > 1.0f) { @@ -249,7 +253,8 @@ void Camera::addArmInertia(f32 player_yaw) m_last_cam_pos.Y = m_camera_direction.Y; m_wieldmesh_offset.Y = rangelim(m_wieldmesh_offset.Y, - WIELDMESH_OFFSET_Y - 10.0f, WIELDMESH_OFFSET_Y + 5.0f); + WIELDMESH_OFFSET_Y - (WIELDMESH_AMPLITUDE_Y * 0.5f), + WIELDMESH_OFFSET_Y + (WIELDMESH_AMPLITUDE_Y * 0.5f)); } m_arm_dir = dir(m_wieldmesh_offset); @@ -259,10 +264,10 @@ void Camera::addArmInertia(f32 player_yaw) following a vector, with a smooth deceleration factor. */ - f32 dec_X = 0.12f * (m_cam_vel_old.X * (1.0f + + f32 dec_X = 0.35f * (std::min(15.0f, m_cam_vel_old.X) * (1.0f + (1.0f - m_arm_dir.X))) * (gap_X / 20.0f); - f32 dec_Y = 0.06f * (m_cam_vel_old.Y * (1.0f + + f32 dec_Y = 0.25f * (std::min(15.0f, m_cam_vel_old.Y) * (1.0f + (1.0f - m_arm_dir.Y))) * (gap_Y / 15.0f); if (gap_X < 0.1f) @@ -285,9 +290,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r // Smooth the movement when walking up stairs v3f old_player_position = m_playernode->getPosition(); v3f player_position = player->getPosition(); - if (player->isAttached && player->parent) - player_position = player->parent->getPosition(); - //if(player->touching_ground && player_position.Y > old_player_position.Y) + + // This is worse than `LocalPlayer::getPosition()` but + // mods expect the player head to be at the parent's position + // plus eye height. + if (player->getParent()) + player_position = player->getParent()->getPosition(); + if(player->touching_ground && player_position.Y > old_player_position.Y) { @@ -561,10 +570,16 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r void Camera::updateViewingRange() { f32 viewing_range = g_settings->getFloat("viewing_range"); - f32 near_plane = g_settings->getFloat("near_plane"); + + // Ignore near_plane setting on all other platforms to prevent abuse +#if ENABLE_GLES + m_cameranode->setNearValue(rangelim( + g_settings->getFloat("near_plane"), 0.0f, 0.25f) * BS); +#else + m_cameranode->setNearValue(0.1f * BS); +#endif m_draw_control.wanted_range = std::fmin(adjustDist(viewing_range, getFovMax()), 4000); - m_cameranode->setNearValue(rangelim(near_plane, 0.0f, 0.5f) * BS); if (m_draw_control.range_all) { m_cameranode->setFarValue(100000.0); return; @@ -592,7 +607,7 @@ void Camera::wield(const ItemStack &item) void Camera::drawWieldedTool(irr::core::matrix4* translation) { - // Clear Z buffer so that the wielded tool stay in front of world geometry + // Clear Z buffer so that the wielded tool stays in front of world geometry m_wieldmgr->getVideoDriver()->clearZBuffer(); // Draw the wielded node (in a separate scene manager) diff --git a/src/client/client.cpp b/src/client/client.cpp index caa3cc78c..c6d28ce80 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -155,6 +155,7 @@ void Client::loadMods() // complain about mods with unsatisfied dependencies if (!modconf.isConsistent()) { modconf.printUnsatisfiedModsError(); + return; } // Print mods @@ -200,14 +201,30 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo std::string full_path = mod_path + DIR_DELIM + mod_subpath; std::vector<fs::DirListNode> mod = fs::GetDirListing(full_path); for (const fs::DirListNode &j : mod) { - std::string filename = j.name; if (j.dir) { - scanModSubfolder(mod_name, mod_path, mod_subpath - + filename + DIR_DELIM); + scanModSubfolder(mod_name, mod_path, mod_subpath + j.name + DIR_DELIM); continue; } - std::replace( mod_subpath.begin(), mod_subpath.end(), DIR_DELIM_CHAR, '/'); - m_mod_files[mod_name + ":" + mod_subpath + filename] = full_path + filename; + std::replace(mod_subpath.begin(), mod_subpath.end(), DIR_DELIM_CHAR, '/'); + + std::string real_path = full_path + j.name; + std::string vfs_path = mod_name + ":" + mod_subpath + j.name; + infostream << "Client::scanModSubfolder(): Loading \"" << real_path + << "\" as \"" << vfs_path << "\"." << std::endl; + + std::ifstream is(real_path, std::ios::binary | std::ios::ate); + if(!is.good()) { + errorstream << "Client::scanModSubfolder(): Can't read file \"" + << real_path << "\"." << std::endl; + continue; + } + auto size = is.tellg(); + std::string contents(size, '\0'); + is.seekg(0); + is.read(&contents[0], size); + + infostream << " size: " << size << " bytes" << std::endl; + m_mod_vfs.emplace(vfs_path, contents); } } @@ -294,6 +311,7 @@ void Client::connect(Address address, bool is_local_server) { initLocalMapSaving(address, m_address_name, is_local_server); + // Since we use TryReceive() a timeout here would be ineffective anyway m_con->SetTimeoutMs(0); m_con->Connect(address); } @@ -764,11 +782,20 @@ void Client::initLocalMapSaving(const Address &address, return; } - const std::string world_path = porting::path_user - + DIR_DELIM + "worlds" - + DIR_DELIM + "server_" + std::string world_path; +#define set_world_path(hostname) \ + world_path = porting::path_user \ + + DIR_DELIM + "worlds" \ + + DIR_DELIM + "server_" \ + hostname + "_" + std::to_string(address.getPort()); + set_world_path(hostname); + if (!fs::IsDir(world_path)) { + std::string hostname_escaped = hostname; + str_replace(hostname_escaped, ':', '_'); + set_world_path(hostname_escaped); + } +#undef set_world_path fs::CreateAllDirs(world_path); m_localdb = new MapDatabaseSQLite3(world_path); @@ -778,36 +805,31 @@ void Client::initLocalMapSaving(const Address &address, void Client::ReceiveAll() { + NetworkPacket pkt; u64 start_ms = porting::getTimeMs(); - for(;;) - { + const u64 budget = 100; + for(;;) { // Limit time even if there would be huge amounts of data to // process - if(porting::getTimeMs() > start_ms + 100) + if (porting::getTimeMs() > start_ms + budget) { + infostream << "Client::ReceiveAll(): " + "Packet processing budget exceeded." << std::endl; break; + } + pkt.clear(); try { - Receive(); - g_profiler->graphAdd("client_received_packets", 1); - } - catch(con::NoIncomingDataException &e) { - break; - } - catch(con::InvalidIncomingDataException &e) { - infostream<<"Client::ReceiveAll(): " + if (!m_con->TryReceive(&pkt)) + break; + ProcessData(&pkt); + } catch (const con::InvalidIncomingDataException &e) { + infostream << "Client::ReceiveAll(): " "InvalidIncomingDataException: what()=" - <<e.what()<<std::endl; + << e.what() << std::endl; } } } -void Client::Receive() -{ - NetworkPacket pkt; - m_con->Receive(&pkt); - ProcessData(&pkt); -} - inline void Client::handleCommand(NetworkPacket* pkt) { const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()]; @@ -824,6 +846,7 @@ void Client::ProcessData(NetworkPacket *pkt) //infostream<<"Client: received command="<<command<<std::endl; m_packetcounter.add((u16)command); + g_profiler->graphAdd("client_received_packets", 1); /* If this check is removed, be sure to change the queue @@ -1083,7 +1106,7 @@ void Client::sendRemovedSounds(std::vector<s32> &soundList) pkt << (u16) (server_ids & 0xFFFF); - for (int sound_id : soundList) + for (s32 sound_id : soundList) pkt << sound_id; Send(&pkt); @@ -1296,7 +1319,7 @@ void Client::removeNode(v3s16 p) * @param is_valid_position * @return */ -MapNode Client::getNode(v3s16 p, bool *is_valid_position) +MapNode Client::CSMGetNode(v3s16 p, bool *is_valid_position) { if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) { v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); @@ -1308,6 +1331,31 @@ MapNode Client::getNode(v3s16 p, bool *is_valid_position) return m_env.getMap().getNode(p, is_valid_position); } +int Client::CSMClampRadius(v3s16 pos, int radius) +{ + if (!checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) + return radius; + // This is approximate and will cause some allowed nodes to be excluded + v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); + u32 distance = ppos.getDistanceFrom(pos); + if (distance >= m_csm_restriction_noderange) + return 0; + return std::min<int>(radius, m_csm_restriction_noderange - distance); +} + +v3s16 Client::CSMClampPos(v3s16 pos) +{ + if (!checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) + return pos; + v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); + const int range = m_csm_restriction_noderange; + return v3s16( + core::clamp<int>(pos.X, (int)ppos.X - range, (int)ppos.X + range), + core::clamp<int>(pos.Y, (int)ppos.Y - range, (int)ppos.Y + range), + core::clamp<int>(pos.Z, (int)ppos.Z - range, (int)ppos.Z + range) + ); +} + void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) { //TimeTaker timer1("Client::addNode()"); @@ -1812,7 +1860,7 @@ ITextureSource* Client::getTextureSource() { return m_tsrc; } -IShaderSource* Client::getShaderSource() +IWritableShaderSource* Client::getShaderSource() { return m_shsrc; } @@ -1864,14 +1912,20 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename, bool cache) return mesh; } -const std::string* Client::getModFile(const std::string &filename) +const std::string* Client::getModFile(std::string filename) { - StringMap::const_iterator it = m_mod_files.find(filename); - if (it == m_mod_files.end()) { - errorstream << "Client::getModFile(): File not found: \"" << filename - << "\"" << std::endl; - return NULL; - } + // strip dir delimiter from beginning of path + auto pos = filename.find_first_of(':'); + if (pos == std::string::npos) + return nullptr; + pos++; + auto pos2 = filename.find_first_not_of('/', pos); + if (pos2 > pos) + filename.erase(pos, pos2 - pos); + + StringMap::const_iterator it = m_mod_vfs.find(filename); + if (it == m_mod_vfs.end()) + return nullptr; return &it->second; } diff --git a/src/client/client.h b/src/client/client.h index e3c931837..1291b944c 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -218,6 +218,9 @@ public: void handleCommand_HudSetFlags(NetworkPacket* pkt); void handleCommand_HudSetParam(NetworkPacket* pkt); void handleCommand_HudSetSky(NetworkPacket* pkt); + void handleCommand_HudSetSun(NetworkPacket* pkt); + void handleCommand_HudSetMoon(NetworkPacket* pkt); + void handleCommand_HudSetStars(NetworkPacket* pkt); void handleCommand_CloudParams(NetworkPacket* pkt); void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt); void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt); @@ -261,14 +264,11 @@ public: // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); - /** - * Helper function for Client Side Modding - * CSM restrictions are applied there, this should not be used for core engine - * @param p - * @param is_valid_position - * @return - */ - MapNode getNode(v3s16 p, bool *is_valid_position); + // helpers to enforce CSM restrictions + MapNode CSMGetNode(v3s16 p, bool *is_valid_position); + int CSMClampRadius(v3s16 pos, int radius); + v3s16 CSMClampPos(v3s16 pos); + void addNode(v3s16 p, MapNode n, bool remove_metadata = true); void setPlayerControl(PlayerControl &control); @@ -370,7 +370,7 @@ public: const NodeDefManager* getNodeDefManager() override; ICraftDefManager* getCraftDefManager() override; ITextureSource* getTextureSource(); - virtual IShaderSource* getShaderSource(); + virtual IWritableShaderSource* getShaderSource(); u16 allocateUnknownNodeId(const std::string &name) override; virtual ISoundManager* getSoundManager(); MtEventManager* getEventManager(); @@ -378,7 +378,7 @@ public: bool checkLocalPrivilege(const std::string &priv) { return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false); - const std::string* getModFile(const std::string &filename); + const std::string* getModFile(std::string filename); std::string getModStoragePath() const override; bool registerModStorage(ModMetadata *meta) override; @@ -413,6 +413,11 @@ public: return m_address_name; } + inline u64 getCSMRestrictionFlags() const + { + return m_csm_restriction_flags; + } + inline bool checkCSMRestrictionFlag(CSMRestrictionFlags flag) const { return m_csm_restriction_flags & flag; @@ -451,7 +456,6 @@ private: bool is_local_server); void ReceiveAll(); - void Receive(); void sendPlayerPos(); @@ -560,7 +564,7 @@ private: std::unordered_map<s32, int> m_sounds_server_to_client; // And the other way! std::unordered_map<int, s32> m_sounds_client_to_server; - // And relations to objects + // Relation of client id to object id std::unordered_map<int, u16> m_sounds_to_objects; // Map server hud ids to client hud ids @@ -576,8 +580,6 @@ private: // Storage for mesh data for creating multiple instances of the same mesh StringMap m_mesh_data; - StringMap m_mod_files; - // own state LocalClientState m_state; @@ -588,11 +590,13 @@ private: IntervalLimiter m_localdb_save_interval; u16 m_cache_save_interval; + // Client modding ClientScripting *m_script = nullptr; bool m_modding_enabled; std::unordered_map<std::string, ModMetadata *> m_mod_storages; float m_mod_storage_save_timer = 10.0f; std::vector<ModSpec> m_mods; + StringMap m_mod_vfs; bool m_shutdown = false; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 5eb033302..52d133781 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -32,11 +32,66 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "raycast.h" #include "voxelalgorithms.h" #include "settings.h" +#include "shader.h" #include "content_cao.h" #include <algorithm> #include "client/renderingengine.h" /* + CAOShaderConstantSetter +*/ + +//! Shader constant setter for passing material emissive color to the CAO object_shader +class CAOShaderConstantSetter : public IShaderConstantSetter +{ +public: + CAOShaderConstantSetter(): + m_emissive_color_setting("emissiveColor") + {} + + ~CAOShaderConstantSetter() override = default; + + void onSetConstants(video::IMaterialRendererServices *services, + bool is_highlevel) override + { + if (!is_highlevel) + return; + + // Ambient color + video::SColorf emissive_color(m_emissive_color); + + float as_array[4] = { + emissive_color.r, + emissive_color.g, + emissive_color.b, + emissive_color.a, + }; + m_emissive_color_setting.set(as_array, services); + } + + void onSetMaterial(const video::SMaterial& material) override + { + m_emissive_color = material.EmissiveColor; + } + +private: + video::SColor m_emissive_color; + CachedPixelShaderSetting<float, 4> m_emissive_color_setting; +}; + +class CAOShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ +public: + CAOShaderConstantSetterFactory() + {} + + virtual IShaderConstantSetter* create() + { + return new CAOShaderConstantSetter(); + } +}; + +/* ClientEnvironment */ @@ -47,6 +102,8 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, m_texturesource(texturesource), m_client(client) { + auto *shdrsrc = m_client->getShaderSource(); + shdrsrc->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory()); } ClientEnvironment::~ClientEnvironment() diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 2a44717ce..f5689c25b 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> #include "irrlichttypes_bloated.h" #include "hud.h" +#include "skyparams.h" enum ClientEventType : u8 { @@ -38,6 +39,9 @@ enum ClientEventType : u8 CE_HUDRM, CE_HUDCHANGE, CE_SET_SKY, + CE_SET_SUN, + CE_SET_MOON, + CE_SET_STARS, CE_OVERRIDE_DAY_NIGHT_RATIO, CE_CLOUD_PARAMS, CLIENTEVENT_MAX, @@ -131,6 +135,7 @@ struct ClientEvent v2f *offset; v3f *world_pos; v2s32 *size; + s16 z_index; } hudadd; struct { @@ -146,13 +151,7 @@ struct ClientEvent v3f *v3fdata; v2s32 *v2s32data; } hudchange; - struct - { - video::SColor *bgcolor; - std::string *type; - std::vector<std::string> *params; - bool clouds; - } set_sky; + SkyboxParams *set_sky; struct { bool do_override; @@ -168,5 +167,8 @@ struct ClientEvent f32 speed_x; f32 speed_y; } cloud_params; + SunParams *sun_params; + MoonParams *moon_params; + StarParams *star_params; }; }; diff --git a/src/client/clientobject.h b/src/client/clientobject.h index c673fff9a..12e0db35b 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -49,8 +49,10 @@ public: virtual bool getSelectionBox(aabb3f *toset) const { return false; } virtual bool collideWithObjects() const { return false; } virtual const v3f getPosition() const { return v3f(0.0f); } - virtual scene::ISceneNode *getSceneNode() { return NULL; } - virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() { return NULL; } + virtual scene::ISceneNode *getSceneNode() const + { return NULL; } + virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() const + { return NULL; } virtual bool isLocalPlayer() const { return false; } virtual ClientActiveObject *getParent() const { return nullptr; }; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 5521a6cf1..d148df522 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include <algorithm> #include <cmath> +#include "client/shader.h" class Settings; struct ToolCapabilities; @@ -352,6 +353,8 @@ void GenericCAO::initialize(const std::string &data) player->setCAO(this); } } + + m_enable_shaders = g_settings->getBool("enable_shaders"); } void GenericCAO::processInitData(const std::string &data) @@ -403,13 +406,17 @@ bool GenericCAO::getSelectionBox(aabb3f *toset) const const v3f GenericCAO::getPosition() const { - if (getParent() != nullptr) { - if (m_matrixnode) - return m_matrixnode->getAbsolutePosition(); + if (!getParent()) + return pos_translator.val_current; - return m_position; + // Calculate real position in world based on MatrixNode + if (m_matrixnode) { + v3s16 camera_offset = m_env->getCameraOffset(); + return m_matrixnode->getAbsolutePosition() + + intToFloat(camera_offset, BS); } - return pos_translator.val_current; + + return m_position; } const bool GenericCAO::isImmortal() @@ -417,7 +424,7 @@ const bool GenericCAO::isImmortal() return itemgroup_get(getGroups(), "immortal"); } -scene::ISceneNode* GenericCAO::getSceneNode() +scene::ISceneNode *GenericCAO::getSceneNode() const { if (m_meshnode) { return m_meshnode; @@ -437,7 +444,7 @@ scene::ISceneNode* GenericCAO::getSceneNode() return NULL; } -scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode() +scene::IAnimatedMeshSceneNode *GenericCAO::getAnimatedMeshSceneNode() const { return m_animated_meshnode; } @@ -573,25 +580,47 @@ void GenericCAO::addToScene(ITextureSource *tsrc) return; } - video::E_MATERIAL_TYPE material_type = (m_prop.use_texture_alpha) ? - video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + if (m_enable_shaders) { + IShaderSource *shader_source = m_client->getShaderSource(); + u32 shader_id = shader_source->getShader( + "object_shader", + TILE_MATERIAL_BASIC, + NDT_NORMAL); + m_material_type = shader_source->getShaderInfo(shader_id).material; + } else { + m_material_type = (m_prop.use_texture_alpha) ? + video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + } - if (m_prop.visual == "sprite") { - infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl; + auto grabMatrixNode = [this] { + infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl; m_matrixnode = RenderingEngine::get_scene_manager()-> addDummyTransformationSceneNode(); m_matrixnode->grab(); + }; + + auto setSceneNodeMaterial = [this] (scene::ISceneNode *node) { + node->setMaterialFlag(video::EMF_LIGHTING, false); + node->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); + node->setMaterialFlag(video::EMF_FOG_ENABLE, true); + node->setMaterialType(m_material_type); + + if (m_enable_shaders) { + node->setMaterialFlag(video::EMF_GOURAUD_SHADING, false); + node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true); + } + }; + + if (m_prop.visual == "sprite") { + grabMatrixNode(); m_spritenode = RenderingEngine::get_scene_manager()->addBillboardSceneNode( m_matrixnode, v2f(1, 1), v3f(0,0,0), -1); m_spritenode->grab(); m_spritenode->setMaterialTexture(0, tsrc->getTextureForMesh("unknown_node.png")); - m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false); - m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); - m_spritenode->setMaterialType(material_type); - m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true); - u8 li = m_last_light; - m_spritenode->setColor(video::SColor(255,li,li,li)); + + setSceneNodeMaterial(m_spritenode); + m_spritenode->setSize(v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); { @@ -601,19 +630,19 @@ void GenericCAO::addToScene(ITextureSource *tsrc) txs, tys, 0, 0); } } else if (m_prop.visual == "upright_sprite") { + grabMatrixNode(); scene::SMesh *mesh = new scene::SMesh(); double dx = BS * m_prop.visual_size.X / 2; double dy = BS * m_prop.visual_size.Y / 2; - u8 li = m_last_light; - video::SColor c(255, li, li, li); + video::SColor c(0xFFFFFFFF); { // Front scene::IMeshBuffer *buf = new scene::SMeshBuffer(); video::S3DVertex vertices[4] = { - video::S3DVertex(-dx, -dy, 0, 0,0,0, c, 1,1), - video::S3DVertex( dx, -dy, 0, 0,0,0, c, 0,1), - video::S3DVertex( dx, dy, 0, 0,0,0, c, 0,0), - video::S3DVertex(-dx, dy, 0, 0,0,0, c, 1,0), + video::S3DVertex(-dx, -dy, 0, 0,0,1, c, 1,1), + video::S3DVertex( dx, -dy, 0, 0,0,1, c, 0,1), + video::S3DVertex( dx, dy, 0, 0,0,1, c, 0,0), + video::S3DVertex(-dx, dy, 0, 0,0,1, c, 1,0), }; if (m_is_player) { // Move minimal Y position to 0 (feet position) @@ -626,7 +655,14 @@ void GenericCAO::addToScene(ITextureSource *tsrc) buf->getMaterial().setFlag(video::EMF_LIGHTING, false); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + buf->getMaterial().MaterialType = m_material_type; + + if (m_enable_shaders) { + buf->getMaterial().EmissiveColor = c; + buf->getMaterial().setFlag(video::EMF_GOURAUD_SHADING, false); + buf->getMaterial().setFlag(video::EMF_NORMALIZE_NORMALS, true); + } + // Add to mesh mesh->addMeshBuffer(buf); buf->drop(); @@ -634,10 +670,10 @@ void GenericCAO::addToScene(ITextureSource *tsrc) { // Back scene::IMeshBuffer *buf = new scene::SMeshBuffer(); video::S3DVertex vertices[4] = { - video::S3DVertex( dx,-dy, 0, 0,0,0, c, 1,1), - video::S3DVertex(-dx,-dy, 0, 0,0,0, c, 0,1), - video::S3DVertex(-dx, dy, 0, 0,0,0, c, 0,0), - video::S3DVertex( dx, dy, 0, 0,0,0, c, 1,0), + video::S3DVertex( dx,-dy, 0, 0,0,-1, c, 1,1), + video::S3DVertex(-dx,-dy, 0, 0,0,-1, c, 0,1), + video::S3DVertex(-dx, dy, 0, 0,0,-1, c, 0,0), + video::S3DVertex( dx, dy, 0, 0,0,-1, c, 1,0), }; if (m_is_player) { // Move minimal Y position to 0 (feet position) @@ -650,14 +686,18 @@ void GenericCAO::addToScene(ITextureSource *tsrc) buf->getMaterial().setFlag(video::EMF_LIGHTING, false); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + buf->getMaterial().MaterialType = m_material_type; + + if (m_enable_shaders) { + buf->getMaterial().EmissiveColor = c; + buf->getMaterial().setFlag(video::EMF_GOURAUD_SHADING, false); + buf->getMaterial().setFlag(video::EMF_NORMALIZE_NORMALS, true); + } + // Add to mesh mesh->addMeshBuffer(buf); buf->drop(); } - m_matrixnode = RenderingEngine::get_scene_manager()-> - addDummyTransformationSceneNode(); - m_matrixnode->grab(); m_meshnode = RenderingEngine::get_scene_manager()-> addMeshSceneNode(mesh, m_matrixnode); m_meshnode->grab(); @@ -666,55 +706,41 @@ void GenericCAO::addToScene(ITextureSource *tsrc) // This is needed for changing the texture in the future m_meshnode->setReadOnlyMaterials(true); } else if (m_prop.visual == "cube") { - infostream<<"GenericCAO::addToScene(): cube"<<std::endl; + grabMatrixNode(); scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS)); - m_matrixnode = RenderingEngine::get_scene_manager()-> - addDummyTransformationSceneNode(nullptr); - m_matrixnode->grab(); m_meshnode = RenderingEngine::get_scene_manager()-> addMeshSceneNode(mesh, m_matrixnode); m_meshnode->grab(); mesh->drop(); m_meshnode->setScale(m_prop.visual_size); - u8 li = m_last_light; - setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li)); - m_meshnode->setMaterialFlag(video::EMF_LIGHTING, false); - m_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); - m_meshnode->setMaterialType(material_type); - m_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true); + setSceneNodeMaterial(m_meshnode); } else if (m_prop.visual == "mesh") { - infostream<<"GenericCAO::addToScene(): mesh"<<std::endl; + grabMatrixNode(); scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh, true); if (mesh) { - m_matrixnode = RenderingEngine::get_scene_manager()-> - addDummyTransformationSceneNode(nullptr); - m_matrixnode->grab(); m_animated_meshnode = RenderingEngine::get_scene_manager()-> addAnimatedMeshSceneNode(mesh, m_matrixnode); m_animated_meshnode->grab(); mesh->drop(); // The scene node took hold of it m_animated_meshnode->animateJoints(); // Needed for some animations m_animated_meshnode->setScale(m_prop.visual_size); - u8 li = m_last_light; // set vertex colors to ensure alpha is set - setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li)); + setMeshColor(m_animated_meshnode->getMesh(), video::SColor(0xFFFFFFFF)); + + setAnimatedMeshColor(m_animated_meshnode, video::SColor(0xFFFFFFFF)); - setAnimatedMeshColor(m_animated_meshnode, video::SColor(255,li,li,li)); + setSceneNodeMaterial(m_animated_meshnode); - m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, true); - m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false); - m_animated_meshnode->setMaterialType(material_type); - m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true); m_animated_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, m_prop.backface_culling); } else errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl; } else if (m_prop.visual == "wielditem" || m_prop.visual == "item") { + grabMatrixNode(); ItemStack item; - infostream << "GenericCAO::addToScene(): wielditem" << std::endl; if (m_prop.wield_item.empty()) { // Old format, only textures are specified. infostream << "textures: " << m_prop.textures.size() << std::endl; @@ -728,18 +754,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc) infostream << "serialized form: " << m_prop.wield_item << std::endl; item.deSerialize(m_prop.wield_item, m_client->idef()); } - m_matrixnode = RenderingEngine::get_scene_manager()-> - addDummyTransformationSceneNode(nullptr); - m_matrixnode->grab(); m_wield_meshnode = new WieldMeshSceneNode( RenderingEngine::get_scene_manager(), -1); - m_wield_meshnode->setParent(m_matrixnode); m_wield_meshnode->setItem(item, m_client, (m_prop.visual == "wielditem")); m_wield_meshnode->setScale(m_prop.visual_size / 2.0f); - u8 li = m_last_light; - m_wield_meshnode->setColor(video::SColor(255, li, li, li)); + m_wield_meshnode->setColor(video::SColor(0xFFFFFFFF)); } else { infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual <<"\" not supported"<<std::endl; @@ -751,6 +772,9 @@ void GenericCAO::addToScene(ITextureSource *tsrc) scene::ISceneNode *node = getSceneNode(); + if (node && m_matrixnode) + node->setParent(m_matrixnode); + if (node && !m_prop.nametag.empty() && !m_is_local_player) { // Add nametag v3f pos; @@ -764,6 +788,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) updateAnimation(); updateBonePosition(); updateAttachments(); + setNodeLight(m_last_light); } void GenericCAO::updateLight(u8 light_at_pos) @@ -790,15 +815,46 @@ void GenericCAO::updateLightNoCheck(u8 light_at_pos) return; u8 li = decode_light(light_at_pos + m_glow); + if (li != m_last_light) { m_last_light = li; - video::SColor color(255,li,li,li); + setNodeLight(li); + } +} + +void GenericCAO::setNodeLight(u8 light) +{ + video::SColor color(255, light, light, light); + + if (m_prop.visual == "wielditem" || m_prop.visual == "item") { + // Since these types of visuals are using their own shader + // they should be handled separately + if (m_wield_meshnode) + m_wield_meshnode->setColor(color); + } else if (m_enable_shaders) { + scene::ISceneNode *node = getSceneNode(); + + if (node == nullptr) + return; + + if (m_prop.visual == "upright_sprite") { + scene::IMesh *mesh = m_meshnode->getMesh(); + for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) { + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + video::SMaterial &material = buf->getMaterial(); + material.EmissiveColor = color; + } + } else { + for (u32 i = 0; i < node->getMaterialCount(); ++i) { + video::SMaterial &material = node->getMaterial(i); + material.EmissiveColor = color; + } + } + } else { if (m_meshnode) { setMeshColor(m_meshnode->getMesh(), color); } else if (m_animated_meshnode) { setAnimatedMeshColor(m_animated_meshnode, color); - } else if (m_wield_meshnode) { - m_wield_meshnode->setColor(color); } else if (m_spritenode) { m_spritenode->setColor(color); } @@ -884,7 +940,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Apply animations if input detected and not attached // or set idle animation - if ((new_anim.X + new_anim.Y) > 0 && !player->isAttached) { + if ((new_anim.X + new_anim.Y) > 0 && !getParent()) { allow_update = true; m_animation_range = new_anim; m_animation_speed = new_speed; @@ -946,12 +1002,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_velocity = v3f(0,0,0); m_acceleration = v3f(0,0,0); pos_translator.val_current = m_position; - - if(m_is_local_player) // Update local player attachment position - { - LocalPlayer *player = m_env->getLocalPlayer(); - player->overridePosition = getParent()->getPosition(); - } + pos_translator.val_target = m_position; } else { rot_translator.translate(dtime); v3f lastpos = pos_translator.val_current; @@ -975,16 +1026,14 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) bool is_end_position = moveresult.collides; pos_translator.update(m_position, is_end_position, dtime); - pos_translator.translate(dtime); - updateNodePos(); } else { m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration; m_velocity += dtime * m_acceleration; pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time); - pos_translator.translate(dtime); - updateNodePos(); } + pos_translator.translate(dtime); + updateNodePos(); float moved = lastpos.getDistanceFrom(pos_translator.val_current); m_step_distance_counter += moved; @@ -1109,16 +1158,13 @@ void GenericCAO::updateTextures(std::string mod) m_current_texture_modifier = mod; m_glow = m_prop.glow; - video::E_MATERIAL_TYPE material_type = (m_prop.use_texture_alpha) ? - video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - if (m_spritenode) { if (m_prop.visual == "sprite") { std::string texturestring = "unknown_node.png"; if (!m_prop.textures.empty()) texturestring = m_prop.textures[0]; texturestring += mod; - m_spritenode->getMaterial(0).MaterialType = material_type; + m_spritenode->getMaterial(0).MaterialType = m_material_type; m_spritenode->getMaterial(0).MaterialTypeParam = 0.5f; m_spritenode->setMaterialTexture(0, tsrc->getTextureForMesh(texturestring)); @@ -1154,7 +1200,7 @@ void GenericCAO::updateTextures(std::string mod) // Set material flags and texture video::SMaterial& material = m_animated_meshnode->getMaterial(i); - material.MaterialType = material_type; + material.MaterialType = m_material_type; material.MaterialTypeParam = 0.5f; material.TextureLayer[0].Texture = texture; material.setFlag(video::EMF_LIGHTING, true); @@ -1201,7 +1247,7 @@ void GenericCAO::updateTextures(std::string mod) // Set material flags and texture video::SMaterial& material = m_meshnode->getMaterial(i); - material.MaterialType = material_type; + material.MaterialType = m_material_type; material.MaterialTypeParam = 0.5f; material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_BILINEAR_FILTER, false); @@ -1348,7 +1394,8 @@ void GenericCAO::updateAttachments() if (!parent) { // Detach or don't attach if (m_matrixnode) { - v3f old_pos = m_matrixnode->getAbsolutePosition(); + v3f old_pos = getPosition(); + m_matrixnode->setParent(m_smgr->getRootSceneNode()); getPosRotMatrix().setTranslation(old_pos); m_matrixnode->updateAbsolutePosition(); @@ -1372,11 +1419,6 @@ void GenericCAO::updateAttachments() m_matrixnode->updateAbsolutePosition(); } } - if (m_is_local_player) { - LocalPlayer *player = m_env->getLocalPlayer(); - player->isAttached = parent; - player->parent = parent; - } } void GenericCAO::processMessage(const std::string &data) diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 2c2d11077..7c29cbf17 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -125,6 +125,10 @@ private: u8 m_last_light = 255; bool m_is_visible = false; s8 m_glow = 0; + // Material + video::E_MATERIAL_TYPE m_material_type; + // Settings + bool m_enable_shaders = false; public: GenericCAO(Client *client, ClientEnvironment *env); @@ -165,9 +169,9 @@ public: const bool isImmortal(); - scene::ISceneNode *getSceneNode(); + scene::ISceneNode *getSceneNode() const; - scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(); + scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() const; // m_matrixnode controls the position and rotation of the child node // for all scene nodes, as a workaround for an Irrlicht problem with @@ -234,6 +238,8 @@ public: void updateLightNoCheck(u8 light_at_pos); + void setNodeLight(u8 light); + v3s16 getLightPosition(); void updateNodePos(); diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 4a0df6171..9b4fd221e 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -556,17 +556,24 @@ void MapblockMeshGenerator::drawLiquidSides() for (int j = 0; j < 4; j++) { const UV &vertex = base_vertices[j]; const v3s16 &base = face.p[vertex.u]; + float v = vertex.v; + v3f pos; - pos.X = (base.X - 0.5) * BS; - pos.Z = (base.Z - 0.5) * BS; - if (vertex.v) - pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS; - else - pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS; + pos.X = (base.X - 0.5f) * BS; + pos.Z = (base.Z - 0.5f) * BS; + if (vertex.v) { + pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5f * BS; + } else if (top_is_same_liquid) { + pos.Y = 0.5f * BS; + } else { + pos.Y = corner_levels[base.Z][base.X]; + v += (0.5f * BS - corner_levels[base.Z][base.X]) / BS; + } + if (data->m_smooth_lighting) color = blendLightColor(pos); pos += origin; - vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v); + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, v); }; collector->append(tile_liquid, vertices, 4, quad_indices, 6); } @@ -860,17 +867,27 @@ void MapblockMeshGenerator::drawTorchlikeNode() for (v3f &vertex : vertices) { switch (wall) { case DWM_YP: - vertex.rotateXZBy(-45); break; + vertex.Y += -size + BS/2; + vertex.rotateXZBy(-45); + break; case DWM_YN: - vertex.rotateXZBy( 45); break; + vertex.Y += size - BS/2; + vertex.rotateXZBy(45); + break; case DWM_XP: - vertex.rotateXZBy( 0); break; + vertex.X += -size + BS/2; + break; case DWM_XN: - vertex.rotateXZBy(180); break; + vertex.X += -size + BS/2; + vertex.rotateXZBy(180); + break; case DWM_ZP: - vertex.rotateXZBy( 90); break; + vertex.X += -size + BS/2; + vertex.rotateXZBy(90); + break; case DWM_ZN: - vertex.rotateXZBy(-90); break; + vertex.X += -size + BS/2; + vertex.rotateXZBy(-90); } } drawQuad(vertices); diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 858d6780e..2b5841cd8 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -59,7 +59,12 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) : if (m_currentMode == FM_Standard) { m_settings->registerChangedCallback("font_size", font_setting_changed, NULL); + m_settings->registerChangedCallback("font_bold", font_setting_changed, NULL); + m_settings->registerChangedCallback("font_italic", font_setting_changed, NULL); m_settings->registerChangedCallback("font_path", font_setting_changed, NULL); + m_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL); + m_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL); + m_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL); m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL); m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL); } @@ -96,36 +101,45 @@ void FontEngine::cleanCache() } /******************************************************************************/ -irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode) +irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec) { - if (mode == FM_Unspecified) { - mode = m_currentMode; + if (spec.mode == FM_Unspecified) { + spec.mode = m_currentMode; } else if (m_currentMode == FM_Simple) { // Freetype disabled -> Force simple mode - mode = (mode == FM_Mono || mode == FM_SimpleMono) ? - FM_SimpleMono : FM_Simple; + spec.mode = (spec.mode == FM_Mono || + spec.mode == FM_SimpleMono) ? + FM_SimpleMono : FM_Simple; + // Support for those could be added, but who cares? + spec.bold = false; + spec.italic = false; } // Fallback to default size - if (font_size == FONT_SIZE_UNSPECIFIED) - font_size = m_default_size[mode]; - - 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); - } + if (spec.size == FONT_SIZE_UNSPECIFIED) + spec.size = m_default_size[spec.mode]; + + const auto &cache = m_font_cache[spec.getHash()]; + auto it = cache.find(spec.size); + if (it != cache.end()) + return it->second; + + // Font does not yet exist + gui::IGUIFont *font = nullptr; + if (spec.mode == FM_Simple || spec.mode == FM_SimpleMono) + font = initSimpleFont(spec); + else + font = initFont(spec); - const auto &font = cache.find(font_size); - return font != cache.end() ? font->second : nullptr; + m_font_cache[spec.getHash()][spec.size] = font; + + return font; } /******************************************************************************/ -unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode) +unsigned int FontEngine::getTextHeight(const FontSpec &spec) { - irr::gui::IGUIFont* font = getFont(font_size, mode); + irr::gui::IGUIFont *font = getFont(spec); // use current skin font as fallback if (font == NULL) { @@ -137,10 +151,9 @@ unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode) } /******************************************************************************/ -unsigned int FontEngine::getTextWidth(const std::wstring& text, - unsigned int font_size, FontMode mode) +unsigned int FontEngine::getTextWidth(const std::wstring &text, const FontSpec &spec) { - irr::gui::IGUIFont* font = getFont(font_size, mode); + irr::gui::IGUIFont *font = getFont(spec); // use current skin font as fallback if (font == NULL) { @@ -153,9 +166,9 @@ unsigned int FontEngine::getTextWidth(const std::wstring& text, /** get line height for a specific font (including empty room between lines) */ -unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode) +unsigned int FontEngine::getLineHeight(const FontSpec &spec) { - irr::gui::IGUIFont* font = getFont(font_size, mode); + irr::gui::IGUIFont *font = getFont(spec); // use current skin font as fallback if (font == NULL) { @@ -181,8 +194,20 @@ void FontEngine::readSettings() m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size"); m_default_size[FM_Mono] = m_settings->getU16("mono_font_size"); + /*~ DO NOT TRANSLATE THIS LITERALLY! + This is a special string. Put either "no" or "yes" + into the translation field (literally). + Choose "yes" if the language requires use of the fallback + font, "no" otherwise. + The fallback font is (normally) required for languages with + non-Latin script, like Chinese. + When in doubt, test your translation. */ m_currentMode = is_yes(gettext("needs_fallback_font")) ? FM_Fallback : FM_Standard; + + m_default_bold = m_settings->getBool("font_bold"); + m_default_italic = m_settings->getBool("font_italic"); + } else { m_currentMode = FM_Simple; } @@ -226,18 +251,14 @@ void FontEngine::updateFontCache() } /******************************************************************************/ -void FontEngine::initFont(unsigned int basesize, FontMode mode) +gui::IGUIFont *FontEngine::initFont(const FontSpec &spec) { - assert(mode != FM_Unspecified); - assert(basesize != FONT_SIZE_UNSPECIFIED); - - if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end()) - return; - + assert(spec.mode != FM_Unspecified); + assert(spec.size != FONT_SIZE_UNSPECIFIED); std::string setting_prefix = ""; - switch (mode) { + switch (spec.mode) { case FM_Fallback: setting_prefix = "fallback_"; break; @@ -249,8 +270,15 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) break; } + std::string setting_suffix = ""; + if (spec.bold) + setting_suffix.append("_bold"); + if (spec.italic) + setting_suffix.append("_italic"); + u32 size = std::floor(RenderingEngine::getDisplayDensity() * - m_settings->getFloat("gui_scaling") * basesize); + m_settings->getFloat("gui_scaling") * spec.size); + if (size == 0) { errorstream << "FontEngine: attempt to use font size 0" << std::endl; errorstream << " display density: " << RenderingEngine::getDisplayDensity() << std::endl; @@ -260,10 +288,14 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) 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); + g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha", + font_shadow_alpha); + + std::string wanted_font_path; + wanted_font_path = g_settings->get(setting_prefix + "font_path" + setting_suffix); std::string fallback_settings[] = { - m_settings->get(setting_prefix + "font_path"), + wanted_font_path, m_settings->get("fallback_font_path"), m_settings->getDefault(setting_prefix + "font_path") }; @@ -274,10 +306,8 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) font_path.c_str(), size, true, true, font_shadow, font_shadow_alpha); - if (font) { - m_font_cache[mode][basesize] = font; - return; - } + if (font) + return font; errorstream << "FontEngine: Cannot load '" << font_path << "'. Trying to fall back to another path." << std::endl; @@ -296,12 +326,13 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) } /** initialize a font without freetype */ -void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) +gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec) { - assert(mode == FM_Simple || mode == FM_SimpleMono); + assert(spec.mode == FM_Simple || spec.mode == FM_SimpleMono); + assert(spec.size != FONT_SIZE_UNSPECIFIED); const std::string &font_path = m_settings->get( - (mode == FM_SimpleMono) ? "mono_font_path" : "font_path"); + (spec.mode == FM_SimpleMono) ? "mono_font_path" : "font_path"); size_t pos_dot = font_path.find_last_of('.'); std::string basename = font_path; @@ -310,19 +341,16 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) if (ending == ".ttf") { errorstream << "FontEngine: Found font \"" << font_path << "\" but freetype is not available." << std::endl; - return; + return nullptr; } if (ending == ".xml" || ending == ".png") basename = font_path.substr(0, pos_dot); - if (basesize == FONT_SIZE_UNSPECIFIED) - basesize = DEFAULT_FONT_SIZE; - u32 size = std::floor( RenderingEngine::getDisplayDensity() * m_settings->getFloat("gui_scaling") * - basesize); + spec.size); irr::gui::IGUIFont *font = nullptr; std::string font_extensions[] = { ".png", ".xml" }; @@ -340,7 +368,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) path.str(""); // Clear path << basename << "_" << (size + offset * sign) << ext; - if (!fs::PathExists(path.str())) + if (!fs::PathExists(path.str())) continue; font = m_env->getFont(path.str().c_str()); @@ -364,6 +392,5 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode) } } - if (font) - m_font_cache[mode][basesize] = font; + return font; } diff --git a/src/client/fontengine.h b/src/client/fontengine.h index 62aa71897..53f14c45f 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define FONT_SIZE_UNSPECIFIED 0xFFFFFFFF -enum FontMode { +enum FontMode : u8 { FM_Standard = 0, FM_Mono, FM_Fallback, @@ -39,6 +39,24 @@ enum FontMode { FM_Unspecified }; +struct FontSpec { + FontSpec(unsigned int font_size, FontMode mode, bool bold, bool italic) : + size(font_size), + mode(mode), + bold(bold), + italic(italic) {} + + u16 getHash() + { + return (mode << 2) | (bold << 1) | italic; + } + + unsigned int size; + FontMode mode; + bool bold; + bool italic; +}; + class FontEngine { public: @@ -47,30 +65,61 @@ public: ~FontEngine(); - /** get Font */ - irr::gui::IGUIFont* getFont(unsigned int font_size=FONT_SIZE_UNSPECIFIED, - FontMode mode=FM_Unspecified); + // Get best possible font specified by FontSpec + irr::gui::IGUIFont *getFont(FontSpec spec); + + irr::gui::IGUIFont *getFont(unsigned int font_size=FONT_SIZE_UNSPECIFIED, + FontMode mode=FM_Unspecified) + { + FontSpec spec(font_size, mode, m_default_bold, m_default_italic); + return getFont(spec); + } /** get text height for a specific font */ - unsigned int getTextHeight(unsigned int font_size=FONT_SIZE_UNSPECIFIED, - FontMode mode=FM_Unspecified); + unsigned int getTextHeight(const FontSpec &spec); /** get text width if a text for a specific font */ - unsigned int getTextWidth(const std::string& text, + unsigned int getTextHeight( unsigned int font_size=FONT_SIZE_UNSPECIFIED, FontMode mode=FM_Unspecified) { - return getTextWidth(utf8_to_wide(text)); + FontSpec spec(font_size, mode, m_default_bold, m_default_italic); + return getTextHeight(spec); } + unsigned int getTextWidth(const std::wstring &text, const FontSpec &spec); + /** get text width if a text for a specific font */ unsigned int getTextWidth(const std::wstring& text, unsigned int font_size=FONT_SIZE_UNSPECIFIED, - FontMode mode=FM_Unspecified); + FontMode mode=FM_Unspecified) + { + FontSpec spec(font_size, mode, m_default_bold, m_default_italic); + return getTextWidth(text, spec); + } + + unsigned int getTextWidth(const std::string &text, const FontSpec &spec) + { + return getTextWidth(utf8_to_wide(text), spec); + } + + unsigned int getTextWidth(const std::string& text, + unsigned int font_size=FONT_SIZE_UNSPECIFIED, + FontMode mode=FM_Unspecified) + { + FontSpec spec(font_size, mode, m_default_bold, m_default_italic); + return getTextWidth(utf8_to_wide(text), spec); + } /** get line height for a specific font (including empty room between lines) */ + unsigned int getLineHeight(const FontSpec &spec); + unsigned int getLineHeight(unsigned int font_size=FONT_SIZE_UNSPECIFIED, - FontMode mode=FM_Unspecified); + FontMode mode=FM_Unspecified) + { + FontSpec spec(font_size, mode, m_default_bold, m_default_italic); + return getLineHeight(spec); + } /** get default font size */ unsigned int getDefaultFontSize(); @@ -86,10 +135,10 @@ private: void updateFontCache(); /** initialize a new font */ - void initFont(unsigned int basesize, FontMode mode=FM_Unspecified); + gui::IGUIFont *initFont(const FontSpec &spec); /** initialize a font without freetype */ - void initSimpleFont(unsigned int basesize, FontMode mode); + gui::IGUIFont *initSimpleFont(const FontSpec &spec); /** update current minetest skin with font changes */ void updateSkin(); @@ -104,11 +153,15 @@ private: gui::IGUIEnvironment* m_env = nullptr; /** internal storage for caching fonts of different size */ - std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode]; + std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode << 2]; /** default font size to use */ unsigned int m_default_size[FM_MaxMode]; + /** default bold and italic */ + bool m_default_bold = false; + bool m_default_italic = false; + /** current font engine mode */ FontMode m_currentMode = FM_Standard; diff --git a/src/client/game.cpp b/src/client/game.cpp index 450eb4e32..0201ded69 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -811,6 +811,9 @@ private: void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam); @@ -2523,6 +2526,9 @@ const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = { {&Game::handleClientEvent_HudRemove}, {&Game::handleClientEvent_HudChange}, {&Game::handleClientEvent_SetSky}, + {&Game::handleClientEvent_SetSun}, + {&Game::handleClientEvent_SetMoon}, + {&Game::handleClientEvent_SetStars}, {&Game::handleClientEvent_OverrideDayNigthRatio}, {&Game::handleClientEvent_CloudParams}, }; @@ -2650,6 +2656,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->offset = *event->hudadd.offset; e->world_pos = *event->hudadd.world_pos; e->size = *event->hudadd.size; + e->z_index = event->hudadd.z_index; hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; @@ -2728,6 +2735,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca case HUD_STAT_SIZE: e->size = *event->hudchange.v2s32data; break; + + case HUD_STAT_Z_INDEX: + e->z_index = event->hudchange.data; + break; } delete event->hudchange.v3fdata; @@ -2739,41 +2750,85 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) { sky->setVisible(false); - // Whether clouds are visible in front of a custom skybox - sky->setCloudsEnabled(event->set_sky.clouds); + // Whether clouds are visible in front of a custom skybox. + sky->setCloudsEnabled(event->set_sky->clouds); if (skybox) { skybox->remove(); skybox = NULL; } - + // Clear the old textures out in case we switch rendering type. + sky->clearSkyboxTextures(); // Handle according to type - if (*event->set_sky.type == "regular") { + if (event->set_sky->type == "regular") { + // Shows the mesh skybox sky->setVisible(true); - sky->setCloudsEnabled(true); - } else if (*event->set_sky.type == "skybox" && - event->set_sky.params->size() == 6) { - sky->setFallbackBgColor(*event->set_sky.bgcolor); - skybox = RenderingEngine::get_scene_manager()->addSkyBoxSceneNode( - texture_src->getTextureForMesh((*event->set_sky.params)[0]), - texture_src->getTextureForMesh((*event->set_sky.params)[1]), - texture_src->getTextureForMesh((*event->set_sky.params)[2]), - texture_src->getTextureForMesh((*event->set_sky.params)[3]), - texture_src->getTextureForMesh((*event->set_sky.params)[4]), - texture_src->getTextureForMesh((*event->set_sky.params)[5])); - } - // Handle everything else as plain color - else { - if (*event->set_sky.type != "plain") + // Update mesh based skybox colours if applicable. + sky->setSkyColors(*event->set_sky); + sky->setHorizonTint( + event->set_sky->sun_tint, + event->set_sky->moon_tint, + event->set_sky->tint_type + ); + } else if (event->set_sky->type == "skybox" && + event->set_sky->textures.size() == 6) { + // Disable the dyanmic mesh skybox: + sky->setVisible(false); + // Set fog colors: + sky->setFallbackBgColor(event->set_sky->bgcolor); + // Set sunrise and sunset fog tinting: + sky->setHorizonTint( + event->set_sky->sun_tint, + event->set_sky->moon_tint, + event->set_sky->tint_type + ); + // Add textures to skybox. + for (int i = 0; i < 6; i++) + sky->addTextureToSkybox(event->set_sky->textures[i], i, texture_src); + } else { + // Handle everything else as plain color. + if (event->set_sky->type != "plain") infostream << "Unknown sky type: " - << (*event->set_sky.type) << std::endl; - - sky->setFallbackBgColor(*event->set_sky.bgcolor); + << (event->set_sky->type) << std::endl; + sky->setVisible(false); + sky->setFallbackBgColor(event->set_sky->bgcolor); + // Disable directional sun/moon tinting on plain or invalid skyboxes. + sky->setHorizonTint( + event->set_sky->bgcolor, + event->set_sky->bgcolor, + "custom" + ); } + delete event->set_sky; +} + +void Game::handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam) +{ + sky->setSunVisible(event->sun_params->visible); + sky->setSunTexture(event->sun_params->texture, + event->sun_params->tonemap, texture_src); + sky->setSunScale(event->sun_params->scale); + sky->setSunriseVisible(event->sun_params->sunrise_visible); + sky->setSunriseTexture(event->sun_params->sunrise, texture_src); + delete event->sun_params; +} - delete event->set_sky.bgcolor; - delete event->set_sky.type; - delete event->set_sky.params; +void Game::handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam) +{ + sky->setMoonVisible(event->moon_params->visible); + sky->setMoonTexture(event->moon_params->texture, + event->moon_params->tonemap, texture_src); + sky->setMoonScale(event->moon_params->scale); + delete event->moon_params; +} + +void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam) +{ + sky->setStarsVisible(event->star_params->visible); + sky->setStarCount(event->star_params->count, false); + sky->setStarColor(event->star_params->starcolor); + sky->setStarScale(event->star_params->scale); + delete event->star_params; } void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, @@ -3061,6 +3116,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) } else if (input->getLeftState()) { // When button is held down in air, show continuous animation runData.left_punch = true; + // Run callback even though item is not usable + if (input->getLeftClicked() && client->modsLoaded()) + client->getScript()->on_item_use(selected_item, pointed); } else if (input->getRightClicked()) { handlePointingAtNothing(selected_item); } @@ -3698,7 +3756,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, video::SColor clouds_dark = clouds->getColor() .getInterpolated(video::SColor(255, 0, 0, 0), 0.9); sky->overrideColors(clouds_dark, clouds->getColor()); - sky->setBodiesVisible(false); + sky->setInClouds(true); runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS); // do not draw clouds after all clouds->setVisible(false); @@ -4126,6 +4184,7 @@ void Game::showPauseMenu() << strgettext("- Creative Mode: ") << creative << "\n"; if (!simple_singleplayer_mode) { const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off; + //~ PvP = Player versus Player os << strgettext("- PvP: ") << pvp << "\n" << strgettext("- Public: ") << announced << "\n"; std::string server_name = g_settings->get("server_name"); diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 674d07fa6..138dfb4da 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -82,7 +82,6 @@ void GameUI::init() 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); } @@ -155,7 +154,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ m_guitext2->setVisible(m_flags.show_debug); - setStaticText(m_guitext_info, translate_string(m_infotext).c_str()); + setStaticText(m_guitext_info, m_infotext.c_str()); m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0); static const float statustext_time_max = 1.5f; @@ -169,7 +168,7 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ } } - setStaticText(m_guitext_status, translate_string(m_statustext).c_str()); + setStaticText(m_guitext_status, m_statustext.c_str()); m_guitext_status->setVisible(!m_statustext.empty()); if (!m_statustext.empty()) { @@ -246,11 +245,12 @@ void GameUI::updateProfiler() int lines = g_profiler->print(os, m_profiler_current_page, m_profiler_max_page); ++lines; - std::wstring text = utf8_to_wide(os.str()); - setStaticText(m_guitext_profiler, text.c_str()); + EnrichedString str(utf8_to_wide(os.str())); + str.setBackground(video::SColor(120, 0, 0, 0)); + setStaticText(m_guitext_profiler, str); core::dimension2d<u32> size = m_guitext_profiler->getOverrideFont()-> - getDimension(text.c_str()); + getDimension(str.c_str()); core::position2di upper_left(6, 50); core::position2di lower_right = upper_left; lower_right.X += size.Width + 10; diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index 2ff57ab74..4262331bd 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -171,7 +171,8 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, } void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, - const core::rect<s32> &rect, const core::rect<s32> &middle) + const core::rect<s32> &rect, const core::rect<s32> &middle, + const core::rect<s32> *cliprect) { const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; @@ -222,9 +223,7 @@ void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, break; } - draw2DImageFilterScaled(driver, texture, dest, - src, - NULL/*&AbsoluteClippingRect*/, colors, true); + draw2DImageFilterScaled(driver, texture, dest, src, cliprect, colors, true); } } } diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index 181009551..b703d91f0 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -53,4 +53,5 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, * 9-slice / segment drawing */ void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, - const core::rect<s32> &rect, const core::rect<s32> &middle); + const core::rect<s32> &rect, const core::rect<s32> &middle, + const core::rect<s32> *cliprect = nullptr); diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 291d03816..37de6640b 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -221,19 +221,13 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, // Store hotbar_image in member variable, used by drawItem() if (hotbar_image != player->hotbar_image) { hotbar_image = player->hotbar_image; - if (!hotbar_image.empty()) - use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image); - else - use_hotbar_image = false; + use_hotbar_image = !hotbar_image.empty(); } // Store hotbar_selected_image in member variable, used by drawItem() if (hotbar_selected_image != player->hotbar_selected_image) { hotbar_selected_image = player->hotbar_selected_image; - if (!hotbar_selected_image.empty()) - use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image); - else - use_hotbar_selected_image = false; + use_hotbar_selected_image = !hotbar_selected_image.empty(); } // draw customized item background @@ -283,11 +277,25 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) { u32 text_height = g_fontengine->getTextHeight(); irr::gui::IGUIFont* font = g_fontengine->getFont(); + + // Reorder elements by z_index + std::vector<size_t> ids; + for (size_t i = 0; i != player->maxHudId(); i++) { HudElement *e = player->getHud(i); if (!e) continue; + auto it = ids.begin(); + while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index) + ++it; + + ids.insert(it, i); + } + + for (size_t i : ids) { + HudElement *e = player->getHud(i); + v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5), floor(e->pos.Y * (float) m_screensize.Y + 0.5)); switch (e->type) { @@ -608,23 +616,24 @@ void Hud::resizeHotbar() { struct MeshTimeInfo { u64 time; - scene::IMesh *mesh; + scene::IMesh *mesh = nullptr; }; -void drawItemStack(video::IVideoDriver *driver, +void drawItemStack( + video::IVideoDriver *driver, gui::IGUIFont *font, const ItemStack &item, const core::rect<s32> &rect, const core::rect<s32> *clip, Client *client, - ItemRotationKind rotation_kind) + ItemRotationKind rotation_kind, + const v3s16 &angle, + const v3s16 &rotation_speed) { static MeshTimeInfo rotation_time_infos[IT_ROT_NONE]; - static thread_local bool enable_animations = - g_settings->getBool("inventory_items_animations"); if (item.empty()) { - if (rotation_kind < IT_ROT_NONE) { + if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) { rotation_time_infos[rotation_kind].mesh = NULL; } return; @@ -639,7 +648,7 @@ void drawItemStack(video::IVideoDriver *driver, s32 delta = 0; if (rotation_kind < IT_ROT_NONE) { MeshTimeInfo &ti = rotation_time_infos[rotation_kind]; - if (mesh != ti.mesh) { + if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) { ti.mesh = mesh; ti.time = porting::getTimeMs(); } else { @@ -677,9 +686,16 @@ void drawItemStack(video::IVideoDriver *driver, core::matrix4 matrix; matrix.makeIdentity(); + static thread_local bool enable_animations = + g_settings->getBool("inventory_items_animations"); + if (enable_animations) { - float timer_f = (float) delta / 5000.0; - matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0)); + float timer_f = (float) delta / 5000.f; + matrix.setRotationDegrees(v3f( + angle.X + rotation_speed.X * 3.60f * timer_f, + angle.Y + rotation_speed.Y * 3.60f * timer_f, + angle.Z + rotation_speed.Z * 3.60f * timer_f) + ); } driver->setTransform(video::ETS_WORLD, matrix); @@ -695,15 +711,18 @@ void drawItemStack(video::IVideoDriver *driver, // because these meshes are not buffered. assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER); video::SColor c = basecolor; + if (imesh->buffer_colors.size() > j) { ItemPartColor *p = &imesh->buffer_colors[j]; if (p->override_base) c = p->color; } + if (imesh->needs_shading) colorizeMeshBuffer(buf, &c); else setMeshBufferColor(buf, c); + video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.Lighting = false; @@ -726,12 +745,12 @@ void drawItemStack(video::IVideoDriver *driver, } } - if(def.type == ITEM_TOOL && item.wear != 0) - { + if (def.type == ITEM_TOOL && item.wear != 0) { // Draw a progressbar - float barheight = rect.getHeight()/16; - float barpad_x = rect.getWidth()/16; - float barpad_y = rect.getHeight()/16; + float barheight = rect.getHeight() / 16; + float barpad_x = rect.getWidth() / 16; + float barpad_y = rect.getHeight() / 16; + core::rect<s32> progressrect( rect.UpperLeftCorner.X + barpad_x, rect.LowerRightCorner.Y - barpad_y - barheight, @@ -739,18 +758,19 @@ void drawItemStack(video::IVideoDriver *driver, rect.LowerRightCorner.Y - barpad_y); // Shrink progressrect by amount of tool damage - float wear = item.wear / 65535.0; + float wear = item.wear / 65535.0f; int progressmid = wear * progressrect.UpperLeftCorner.X + - (1-wear) * progressrect.LowerRightCorner.X; + (1 - wear) * progressrect.LowerRightCorner.X; // Compute progressbar color // wear = 0.0: green // wear = 0.5: yellow // wear = 1.0: red - video::SColor color(255,255,255,255); + video::SColor color(255, 255, 255, 255); int wear_i = MYMIN(std::floor(wear * 600), 511); wear_i = MYMIN(wear_i + 10, 511); + if (wear_i <= 255) color.set(255, wear_i, 255, 0); else @@ -760,18 +780,17 @@ void drawItemStack(video::IVideoDriver *driver, progressrect2.LowerRightCorner.X = progressmid; driver->draw2DRectangle(color, progressrect2, clip); - color = video::SColor(255,0,0,0); + color = video::SColor(255, 0, 0, 0); progressrect2 = progressrect; progressrect2.UpperLeftCorner.X = progressmid; driver->draw2DRectangle(color, progressrect2, clip); } - if(font != NULL && item.count >= 2) - { + if (font != NULL && item.count >= 2) { // Get the item count as a string std::string text = itos(item.count); v2u32 dim = font->getDimension(utf8_to_wide(text).c_str()); - v2s32 sdim(dim.X,dim.Y); + v2s32 sdim(dim.X, dim.Y); core::rect<s32> rect2( /*rect.UpperLeftCorner, @@ -780,10 +799,23 @@ void drawItemStack(video::IVideoDriver *driver, sdim ); - video::SColor bgcolor(128,0,0,0); + video::SColor bgcolor(128, 0, 0, 0); driver->draw2DRectangle(bgcolor, rect2, clip); - video::SColor color(255,255,255,255); + video::SColor color(255, 255, 255, 255); font->draw(text.c_str(), rect2, color, false, false, clip); } } + +void drawItemStack( + video::IVideoDriver *driver, + gui::IGUIFont *font, + const ItemStack &item, + const core::rect<s32> &rect, + const core::rect<s32> *clip, + Client *client, + ItemRotationKind rotation_kind) +{ + drawItemStack(driver, font, item, rect, clip, client, rotation_kind, + v3s16(0, 0, 0), v3s16(0, 100, 0)); +} diff --git a/src/client/hud.h b/src/client/hud.h index 693d2adee..d9b5e0686 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -122,6 +122,7 @@ enum ItemRotationKind IT_ROT_SELECTED, IT_ROT_HOVERED, IT_ROT_DRAGGED, + IT_ROT_OTHER, IT_ROT_NONE, // Must be last, also serves as number }; @@ -133,4 +134,15 @@ void drawItemStack(video::IVideoDriver *driver, Client *client, ItemRotationKind rotation_kind); +void drawItemStack( + video::IVideoDriver *driver, + gui::IGUIFont *font, + const ItemStack &item, + const core::rect<s32> &rect, + const core::rect<s32> *clip, + Client *client, + ItemRotationKind rotation_kind, + const v3s16 &angle, + const v3s16 &rotation_speed); + #endif diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp index 646d181e0..6a0e9f569 100644 --- a/src/client/keycode.cpp +++ b/src/client/keycode.cpp @@ -109,6 +109,7 @@ static const struct table_key table[] = { DEFINEKEY1(KEY_RETURN, N_("Return")) DEFINEKEY1(KEY_SHIFT, N_("Shift")) DEFINEKEY1(KEY_CONTROL, N_("Control")) + //~ Key name, common on Windows keyboards DEFINEKEY1(KEY_MENU, N_("Menu")) DEFINEKEY1(KEY_PAUSE, N_("Pause")) DEFINEKEY1(KEY_CAPITAL, N_("Caps Lock")) @@ -121,7 +122,9 @@ static const struct table_key table[] = { DEFINEKEY1(KEY_UP, N_("Up")) DEFINEKEY1(KEY_RIGHT, N_("Right")) DEFINEKEY1(KEY_DOWN, N_("Down")) + //~ Key name DEFINEKEY1(KEY_SELECT, N_("Select")) + //~ "Print screen" key DEFINEKEY1(KEY_PRINT, N_("Print")) DEFINEKEY1(KEY_EXECUT, N_("Execute")) DEFINEKEY1(KEY_SNAPSHOT, N_("Snapshot")) diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index c086d860a..c20c3619f 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -184,8 +184,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f position = getPosition(); // Copy parent position if local player is attached - if (isAttached) { - setPosition(overridePosition); + if (getParent()) { + setPosition(m_cao->getPosition()); added_velocity = v3f(0.0f); // ignored return; } @@ -474,7 +474,7 @@ 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) { + if (getParent()) { setSpeed(v3f(0.0f)); return; } @@ -706,6 +706,11 @@ v3f LocalPlayer::getEyeOffset() const return v3f(0.0f, BS * eye_height, 0.0f); } +ClientActiveObject *LocalPlayer::getParent() const +{ + return m_cao ? m_cao->getParent() : nullptr; +} + bool LocalPlayer::isDead() const { FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized"); @@ -764,8 +769,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, v3f position = getPosition(); // Copy parent position if local player is attached - if (isAttached) { - setPosition(overridePosition); + if (getParent()) { + setPosition(m_cao->getPosition()); m_sneak_node_exists = false; added_velocity = v3f(0.0f); return; diff --git a/src/client/localplayer.h b/src/client/localplayer.h index 45dc6776e..95dceb1f4 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -47,12 +47,9 @@ public: LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer() = default; - ClientActiveObject *parent = nullptr; - // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points u16 hp = 0; - bool isAttached = false; bool touching_ground = false; // This oscillates so that the player jumps a bit above the surface bool in_liquid = false; @@ -72,8 +69,6 @@ public: // Temporary option for old move code bool physics_override_new_move = true; - v3f overridePosition; - void move(f32 dtime, Environment *env, f32 pos_max_d); void move(f32 dtime, Environment *env, f32 pos_max_d, std::vector<CollisionInfo> *collision_info); @@ -112,6 +107,8 @@ public: GenericCAO *getCAO() const { return m_cao; } + ClientActiveObject *getParent() const; + void setCAO(GenericCAO *toset) { assert(!m_cao); // Pre-condition diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 2bfaa7a4f..a5bee6b88 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -795,6 +795,7 @@ static void getTileInfo( v3s16 &p_corrected, v3s16 &face_dir_corrected, u16 *lights, + u8 &waving, TileSpec &tile ) { @@ -842,6 +843,7 @@ static void getTileInfo( getNodeTile(n, p_corrected, face_dir_corrected, data, tile); const ContentFeatures &f = ndef->get(n); + waving = f.waving; tile.emissive_light = f.light_source; // eg. water and glass @@ -876,6 +878,10 @@ static void updateFastFaceRow( const v3s16 &&face_dir, std::vector<FastFace> &dest) { + static thread_local const bool waving_liquids = + g_settings->getBool("enable_shaders") && + g_settings->getBool("enable_waving_water"); + v3s16 p = startpos; u16 continuous_tiles_count = 1; @@ -884,10 +890,11 @@ static void updateFastFaceRow( v3s16 p_corrected; v3s16 face_dir_corrected; u16 lights[4] = {0, 0, 0, 0}; + u8 waving; TileSpec tile; getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected, - lights, tile); + lights, waving, tile); // Unroll this variable which has a significant build cost TileSpec next_tile; @@ -910,12 +917,15 @@ static void updateFastFaceRow( getTileInfo(data, p_next, face_dir, next_makes_face, next_p_corrected, next_face_dir_corrected, next_lights, + waving, next_tile); if (next_makes_face == makes_face && next_p_corrected == p_corrected + translate_dir && next_face_dir_corrected == face_dir_corrected && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0 + // Don't apply fast faces to waving water. + && (waving != 3 || !waving_liquids) && next_tile.isTileable(tile)) { next_is_different = false; continuous_tiles_count++; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index f36ff3d85..eda415ce6 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -188,7 +188,7 @@ public: delete setter; } - virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) + virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override { video::IVideoDriver *driver = services->getVideoDriver(); sanity_check(driver != NULL); @@ -198,6 +198,12 @@ public: for (IShaderConstantSetter *setter : m_setters) setter->onSetConstants(services, is_highlevel); } + + virtual void OnSetMaterial(const video::SMaterial& material) override + { + for (IShaderConstantSetter *setter : m_setters) + setter->onSetMaterial(material); + } }; diff --git a/src/client/shader.h b/src/client/shader.h index 583c776f4..109d39336 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -67,6 +67,8 @@ public: virtual ~IShaderConstantSetter() = default; virtual void onSetConstants(video::IMaterialRendererServices *services, bool is_highlevel) = 0; + virtual void onSetMaterial(const video::SMaterial& material) + { } }; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 346cd0642..7a7b188ce 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -18,22 +18,23 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "sky.h" +#include "ITexture.h" #include "IVideoDriver.h" #include "ISceneManager.h" #include "ICameraSceneNode.h" #include "S3DVertex.h" #include "client/tile.h" -#include "noise.h" // easeCurve +#include "noise.h" // easeCurve #include "profiler.h" #include "util/numeric.h" #include <cmath> #include "client/renderingengine.h" #include "settings.h" -#include "camera.h" // CameraModes +#include "camera.h" // CameraModes #include "config.h" +using namespace irr::core; - -Sky::Sky(s32 id, ITextureSource *tsrc): +Sky::Sky(s32 id, ITextureSource *tsrc) : scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), RenderingEngine::get_scene_manager(), id) { @@ -67,44 +68,51 @@ Sky::Sky(s32 id, ITextureSource *tsrc): m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; //m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; - m_sun_texture = tsrc->isKnownSourceImage("sun.png") ? - tsrc->getTextureForMesh("sun.png") : NULL; - m_moon_texture = tsrc->isKnownSourceImage("moon.png") ? - tsrc->getTextureForMesh("moon.png") : NULL; - m_sun_tonemap = tsrc->isKnownSourceImage("sun_tonemap.png") ? - tsrc->getTexture("sun_tonemap.png") : NULL; - m_moon_tonemap = tsrc->isKnownSourceImage("moon_tonemap.png") ? - tsrc->getTexture("moon_tonemap.png") : NULL; + // Ensures that sun and moon textures and tonemaps are correct. + setSkyDefaults(); + m_sun_texture = tsrc->isKnownSourceImage(m_sun_params.texture) ? + tsrc->getTextureForMesh(m_sun_params.texture) : NULL; + m_moon_texture = tsrc->isKnownSourceImage(m_moon_params.texture) ? + tsrc->getTextureForMesh(m_moon_params.texture) : NULL; + m_sun_tonemap = tsrc->isKnownSourceImage(m_sun_params.tonemap) ? + tsrc->getTexture(m_sun_params.tonemap) : NULL; + m_moon_tonemap = tsrc->isKnownSourceImage(m_moon_params.tonemap) ? + tsrc->getTexture(m_moon_params.tonemap) : NULL; if (m_sun_texture) { m_materials[3] = mat; m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Disables texture filtering + m_materials[3].setFlag(video::E_MATERIAL_FLAG::EMF_BILINEAR_FILTER, false); + m_materials[3].setFlag(video::E_MATERIAL_FLAG::EMF_TRILINEAR_FILTER, false); + m_materials[3].setFlag(video::E_MATERIAL_FLAG::EMF_ANISOTROPIC_FILTER, false); + // Use tonemaps if available if (m_sun_tonemap) m_materials[3].Lighting = true; } - if (m_moon_texture) { m_materials[4] = mat; m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Disables texture filtering + m_materials[4].setFlag(video::E_MATERIAL_FLAG::EMF_BILINEAR_FILTER, false); + m_materials[4].setFlag(video::E_MATERIAL_FLAG::EMF_TRILINEAR_FILTER, false); + m_materials[4].setFlag(video::E_MATERIAL_FLAG::EMF_ANISOTROPIC_FILTER, false); + // Use tonemaps if available if (m_moon_tonemap) m_materials[4].Lighting = true; } - for (v3f &star : m_stars) { - star = v3f( - myrand_range(-10000, 10000), - myrand_range(-10000, 10000), - myrand_range(-10000, 10000) - ); - star.normalize(); + for (int i = 5; i < 11; i++) { + m_materials[i] = mat; + m_materials[i].Lighting = true; + m_materials[i].MaterialType = video::EMT_SOLID; } - m_directional_colored_fog = g_settings->getBool("directional_colored_fog"); + setStarCount(1000, true); } - void Sky::OnRegisterSceneNode() { if (IsVisible) @@ -113,12 +121,8 @@ void Sky::OnRegisterSceneNode() scene::ISceneNode::OnRegisterSceneNode(); } - void Sky::render() { - if (!m_visible) - return; - video::IVideoDriver *driver = SceneManager->getVideoDriver(); scene::ICameraSceneNode *camera = SceneManager->getActiveCamera(); @@ -205,143 +209,103 @@ void Sky::render() video::SColor cloudyfogcolor = m_bgcolor; - // Draw far cloudy fog thing blended with skycolor - for (u32 j = 0; j < 4; j++) { - video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45); - vertices[0] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.12, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.12, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - if (j == 0) - // Don't switch - {} - else if (j == 1) - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - else if (j == 2) - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - else - // Switch from -Z (south) to +Z (north) - vertex.Pos.rotateXZBy(-180); - } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); - } + // Abort rendering if we're in the clouds. + // Stops rendering a pure white hole in the bottom of the skybox. + if (m_in_clouds) + return; - // Draw far cloudy fog thing at and below all horizons - for (u32 j = 0; j < 4; j++) { - video::SColor c = cloudyfogcolor; - vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - if (j == 0) - // Don't switch - {} - else if (j == 1) - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - else if (j == 2) - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - else - // Switch from -Z (south) to +Z (north) - vertex.Pos.rotateXZBy(-180); + // Draw the six sided skybox, + if (m_sky_params.textures.size() == 6) { + for (u32 j = 5; j < 11; j++) { + video::SColor c(255, 255, 255, 255); + driver->setMaterial(m_materials[j]); + // Use 1.05 rather than 1.0 to avoid colliding with the + // sun, moon and stars, as this is a background skybox. + vertices[0] = video::S3DVertex(-1.05, -1.05, -1.05, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1.05, -1.05, -1.05, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1.05, 1.05, -1.05, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1.05, 1.05, -1.05, 0, 0, 1, c, t, o); + for (video::S3DVertex &vertex : vertices) { + if (j == 5) { // Top texture + vertex.Pos.rotateYZBy(90); + vertex.Pos.rotateXZBy(90); + } else if (j == 6) { // Bottom texture + vertex.Pos.rotateYZBy(-90); + vertex.Pos.rotateXZBy(90); + } else if (j == 7) { // Left texture + vertex.Pos.rotateXZBy(90); + } else if (j == 8) { // Right texture + vertex.Pos.rotateXZBy(-90); + } else if (j == 9) { // Front texture, do nothing + // Irrlicht doesn't like it when vertexes are left + // alone and not rotated for some reason. + vertex.Pos.rotateXZBy(0); + } else {// Back texture + vertex.Pos.rotateXZBy(180); + } + } + driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - // If sun, moon and stars are (temporarily) disabled, abort here - if (!m_bodies_visible) - return; - - // Draw stars before sun and moon to be behind them - do { + // Draw far cloudy fog thing blended with skycolor + if (m_visible) { driver->setMaterial(m_materials[1]); - // Tune values so that stars first appear just after the sun - // disappears over the horizon, and disappear just before the sun - // appears over the horizon. - // Also tune so that stars are at full brightness from time 20000 to - // time 4000. - float starbrightness = MYMAX(0, MYMIN(1, - (0.25 - fabs(wicked_time_of_day < 0.5 ? - wicked_time_of_day : (1.0 - wicked_time_of_day))) * 20)); - float f = starbrightness; - float d = 0.007 / 2; - video::SColor starcolor(255, f * 90, f * 90, f * 90); - // Stars are only drawn when brighter than skycolor - if (starcolor.getBlue() < m_skycolor.getBlue()) - break; -#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++) { - indices[i * 3 + 0] = i * 3 + 0; - indices[i * 3 + 1] = i * 3 + 1; - indices[i * 3 + 2] = i * 3 + 2; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, 0); - v3f p2 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 3 + 0].Pos = p; - vertices[i * 3 + 0].Color = starcolor; - vertices[i * 3 + 1].Pos = p1; - vertices[i * 3 + 1].Color = starcolor; - vertices[i * 3 + 2].Pos = p2; - vertices[i * 3 + 2].Color = starcolor; + for (u32 j = 0; j < 4; j++) { + video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45); + vertices[0] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1, 0.12, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1, 0.12, -1, 0, 0, 1, c, t, o); + for (video::S3DVertex &vertex : vertices) { + if (j == 0) + // Don't switch + {} + else if (j == 1) + // Switch from -Z (south) to +X (east) + vertex.Pos.rotateXZBy(90); + else if (j == 2) + // Switch from -Z (south) to -X (west) + vertex.Pos.rotateXZBy(-90); + else + // Switch from -Z (south) to +Z (north) + vertex.Pos.rotateXZBy(-180); + } + driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - driver->drawIndexedTriangleList(vertices, SKY_STAR_COUNT * 3, - indices, SKY_STAR_COUNT); -#else - u16 indices[SKY_STAR_COUNT * 4]; - video::S3DVertex vertices[SKY_STAR_COUNT * 4]; - for (u32 i = 0; i < SKY_STAR_COUNT; i++) { - indices[i * 4 + 0] = i * 4 + 0; - indices[i * 4 + 1] = i * 4 + 1; - indices[i * 4 + 2] = i * 4 + 2; - indices[i * 4 + 3] = i * 4 + 3; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f( d, 1, -d); - v3f p2 = v3f( d, 1, d); - v3f p3 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - a.rotateVect(p3); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - p3.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 4 + 0].Pos = p; - vertices[i * 4 + 0].Color = starcolor; - vertices[i * 4 + 1].Pos = p1; - vertices[i * 4 + 1].Color = starcolor; - vertices[i * 4 + 2].Pos = p2; - vertices[i * 4 + 2].Color = starcolor; - vertices[i * 4 + 3].Pos = p3; - vertices[i * 4 + 3].Color = starcolor; + + // Draw far cloudy fog thing at and below all horizons + for (u32 j = 0; j < 4; j++) { + video::SColor c = cloudyfogcolor; + vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, o); + for (video::S3DVertex &vertex : vertices) { + if (j == 0) + // Don't switch + {} + else if (j == 1) + // Switch from -Z (south) to +X (east) + vertex.Pos.rotateXZBy(90); + else if (j == 2) + // Switch from -Z (south) to -X (west) + vertex.Pos.rotateXZBy(-90); + else + // Switch from -Z (south) to +Z (north) + vertex.Pos.rotateXZBy(-180); + } + driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - driver->drawVertexPrimitiveList(vertices, SKY_STAR_COUNT * 4, - indices, SKY_STAR_COUNT, video::EVT_STANDARD, - scene::EPT_QUADS, video::EIT_16BIT); -#endif - } while (false); + } + + // Draw stars before sun and moon to be behind them + if (m_star_params.visible) + draw_stars(driver, wicked_time_of_day); - // Draw sunrise/sunset horizon glow texture (textures/base/pack/sunrisebg.png) - { + // Draw sunrise/sunset horizon glow texture + // (textures/base/pack/sunrisebg.png) + if (m_sun_params.sunrise_visible) { driver->setMaterial(m_materials[2]); float mid1 = 0.25; float mid = wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1); @@ -366,53 +330,52 @@ void Sky::render() } // Draw sun - if (wicked_time_of_day > 0.15 && wicked_time_of_day < 0.85) { + if (m_sun_params.visible) 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_params.visible) draw_moon(driver, moonsize, mooncolor, mooncolor2, wicked_time_of_day); - } // Draw far cloudy fog thing below all horizons in front of sun, moon // and stars. - driver->setMaterial(m_materials[1]); + if (m_visible) { + driver->setMaterial(m_materials[1]); - for (u32 j = 0; j < 4; j++) { - video::SColor c = cloudyfogcolor; - vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); - vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); - vertices[2] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, c, o, o); - vertices[3] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, c, t, o); - for (video::S3DVertex &vertex : vertices) { - if (j == 0) - // Don't switch - {} - else if (j == 1) - // Switch from -Z (south) to +X (east) - vertex.Pos.rotateXZBy(90); - else if (j == 2) - // Switch from -Z (south) to -X (west) - vertex.Pos.rotateXZBy(-90); - else - // Switch from -Z (south) to +Z (north) - vertex.Pos.rotateXZBy(-180); + for (u32 j = 0; j < 4; j++) { + video::SColor c = cloudyfogcolor; + vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, c, t, o); + for (video::S3DVertex &vertex : vertices) { + if (j == 0) + // Don't switch + {} + else if (j == 1) + // Switch from -Z (south) to +X (east) + vertex.Pos.rotateXZBy(90); + else if (j == 2) + // Switch from -Z (south) to -X (west) + vertex.Pos.rotateXZBy(-90); + else + // Switch from -Z (south) to +Z (north) + vertex.Pos.rotateXZBy(-180); + } + driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } + + // Draw bottom far cloudy fog thing in front of sun, moon and stars + video::SColor c = cloudyfogcolor; + vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 1, 0, c, t, t); + vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t); + vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o); + vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o); driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - - // Draw bottom far cloudy fog thing in front of sun, moon and stars - video::SColor c = cloudyfogcolor; - vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 1, 0, c, t, t); - vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t); - vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o); - vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } } - void Sky::update(float time_of_day, float time_brightness, float direct_brightness, bool sunlight_seen, CameraMode cam_mode, float yaw, float pitch) @@ -426,7 +389,7 @@ void Sky::update(float time_of_day, float time_brightness, m_first_update = false; for (u32 i = 0; i < 100; i++) { update(time_of_day, time_brightness, direct_brightness, - sunlight_seen, cam_mode, yaw, pitch); + sunlight_seen, cam_mode, yaw, pitch); } return; } @@ -434,7 +397,7 @@ void Sky::update(float time_of_day, float time_brightness, m_time_of_day = time_of_day; m_time_brightness = time_brightness; m_sunlight_seen = sunlight_seen; - m_bodies_visible = true; + m_in_clouds = false; bool is_dawn = (time_brightness >= 0.20 && time_brightness < 0.35); @@ -452,19 +415,17 @@ void Sky::update(float time_of_day, float time_brightness, video::SColorf cloudcolor_bright_dawn_f(1.0, 0.7, 0.5); */ - video::SColorf bgcolor_bright_normal_f = video::SColor(255, 155, 193, 240); - video::SColorf bgcolor_bright_indoor_f = video::SColor(255, 100, 100, 100); - video::SColorf bgcolor_bright_dawn_f = video::SColor(255, 186, 193, 240); - video::SColorf bgcolor_bright_night_f = video::SColor(255, 64, 144, 255); + video::SColorf bgcolor_bright_normal_f = m_sky_params.sky_color.day_horizon; + video::SColorf bgcolor_bright_indoor_f = m_sky_params.sky_color.indoors; + video::SColorf bgcolor_bright_dawn_f = m_sky_params.sky_color.dawn_horizon; + video::SColorf bgcolor_bright_night_f = m_sky_params.sky_color.night_horizon; - video::SColorf skycolor_bright_normal_f = video::SColor(255, 140, 186, 250); - video::SColorf skycolor_bright_dawn_f = video::SColor(255, 180, 186, 250); - video::SColorf skycolor_bright_night_f = video::SColor(255, 0, 107, 255); + video::SColorf skycolor_bright_normal_f = m_sky_params.sky_color.day_sky; + video::SColorf skycolor_bright_dawn_f = m_sky_params.sky_color.dawn_sky; + video::SColorf skycolor_bright_night_f = m_sky_params.sky_color.night_sky; - // pure white: becomes "diffuse light component" for clouds - video::SColorf cloudcolor_bright_normal_f = video::SColor(255, 255, 255, 255); - // dawn-factoring version of pure white (note: R is above 1.0) - video::SColorf cloudcolor_bright_dawn_f(255.0f/240.0f, 223.0f/240.0f, 191.0f/255.0f); + video::SColorf cloudcolor_bright_normal_f = m_cloudcolor_day_f; + video::SColorf cloudcolor_bright_dawn_f = m_cloudcolor_dawn_f; float cloud_color_change_fraction = 0.95; if (sunlight_seen) { @@ -558,13 +519,17 @@ void Sky::update(float time_of_day, float time_brightness, f32 pointcolor_light = rangelim(m_time_brightness * 3, 0.2, 1); video::SColorf pointcolor_sun_f(1, 1, 1, 1); - if (m_sun_tonemap) { + // Use tonemap only if default sun/moon tinting is used + // which keeps previous behaviour. + if (m_sun_tonemap && m_default_tint) { pointcolor_sun_f.r = pointcolor_light * (float)m_materials[3].EmissiveColor.getRed() / 255; pointcolor_sun_f.b = pointcolor_light * (float)m_materials[3].EmissiveColor.getBlue() / 255; pointcolor_sun_f.g = pointcolor_light * (float)m_materials[3].EmissiveColor.getGreen() / 255; + } else if (!m_default_tint) { + pointcolor_sun_f = m_sky_params.sun_tint; } else { pointcolor_sun_f.r = pointcolor_light * 1; pointcolor_sun_f.b = pointcolor_light * @@ -573,9 +538,23 @@ void Sky::update(float time_of_day, float time_brightness, (rangelim(m_time_brightness, 0.05, 0.15) - 0.05) * 10 * 0.625); } - video::SColorf pointcolor_moon_f(0.5 * pointcolor_light, - 0.6 * pointcolor_light, 0.8 * pointcolor_light, 1); - if (m_moon_tonemap) { + video::SColorf pointcolor_moon_f; + if (m_default_tint) { + pointcolor_moon_f = video::SColorf( + 0.5 * pointcolor_light, + 0.6 * pointcolor_light, + 0.8 * pointcolor_light, + 1 + ); + } else { + pointcolor_moon_f = video::SColorf( + (m_sky_params.moon_tint.getRed() / 255) * pointcolor_light, + (m_sky_params.moon_tint.getGreen() / 255) * pointcolor_light, + (m_sky_params.moon_tint.getBlue() / 255) * pointcolor_light, + 1 + ); + } + if (m_moon_tonemap && m_default_tint) { pointcolor_moon_f.r = pointcolor_light * (float)m_materials[4].EmissiveColor.getRed() / 255; pointcolor_moon_f.b = pointcolor_light * @@ -640,7 +619,12 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol 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}; + const float sunsizes[4] = { + (sunsize * 1.7f) * m_sun_params.scale, + (sunsize * 1.2f) * m_sun_params.scale, + (sunsize) * m_sun_params.scale, + (sunsize * 0.7f) * m_sun_params.scale + }; video::SColor c1 = suncolor; video::SColor c2 = suncolor; c1.setAlpha(0.05 * 255); @@ -653,7 +637,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol } } else { driver->setMaterial(m_materials[3]); - float d = sunsize * 1.7; + float d = (sunsize * 1.7) * m_sun_params.scale; video::SColor c; if (m_sun_tonemap) c = video::SColor(0, 0, 0, 0); @@ -668,31 +652,32 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol 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 - */ +/* + * 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 - }; + (-moonsize * 1.9f) * m_moon_params.scale, + (-moonsize * 1.3f) * m_moon_params.scale, + (-moonsize) * m_moon_params.scale, + (-moonsize) * m_moon_params.scale + }; const float moonsizes_2[4] = { - moonsize * 1.9f, - moonsize * 1.3f, - moonsize, - moonsize * 0.6f - }; + (moonsize * 1.9f) * m_moon_params.scale, + (moonsize * 1.3f) * m_moon_params.scale, + (moonsize) *m_moon_params.scale, + (moonsize * 0.6f) * m_moon_params.scale + }; video::SColor c1 = mooncolor; video::SColor c2 = mooncolor; c1.setAlpha(0.05 * 255); @@ -705,7 +690,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC } } else { driver->setMaterial(m_materials[4]); - float d = moonsize * 1.9; + float d = (moonsize * 1.9) * m_moon_params.scale; video::SColor c; if (m_moon_tonemap) c = video::SColor(0, 0, 0, 0); @@ -717,14 +702,106 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC } } +void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) +{ + driver->setMaterial(m_materials[1]); + // Tune values so that stars first appear just after the sun + // disappears over the horizon, and disappear just before the sun + // appears over the horizon. + // Also tune so that stars are at full brightness from time 20000 + // to time 4000. + + float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day); + float starbrightness = clamp((0.25f - fabsf(tod)) * 20.0f, 0.0f, 1.0f); + + float f = starbrightness; + float d = (0.006 / 2) * m_star_params.scale; + + video::SColor starcolor = m_star_params.starcolor; + starcolor.setAlpha(f * m_star_params.starcolor.getAlpha()); + + // Stars are only drawn when not fully transparent + if (m_star_params.starcolor.getAlpha() < 1) + return; +#if ENABLE_GLES + u16 *indices = new u16[m_star_params.count * 3]; + video::S3DVertex *vertices = + new video::S3DVertex[m_star_params.count * 3]; + for (u32 i = 0; i < m_star_params.count; i++) { + indices[i * 3 + 0] = i * 3 + 0; + indices[i * 3 + 1] = i * 3 + 1; + indices[i * 3 + 2] = i * 3 + 2; + v3f r = m_stars[i]; + core::CMatrix4<f32> a; + a.buildRotateFromTo(v3f(0, 1, 0), r); + v3f p = v3f(-d, 1, -d); + v3f p1 = v3f(d, 1, 0); + v3f p2 = v3f(-d, 1, d); + a.rotateVect(p); + a.rotateVect(p1); + a.rotateVect(p2); + p.rotateXYBy(wicked_time_of_day * 360 - 90); + p1.rotateXYBy(wicked_time_of_day * 360 - 90); + p2.rotateXYBy(wicked_time_of_day * 360 - 90); + vertices[i * 3 + 0].Pos = p; + vertices[i * 3 + 0].Color = starcolor; + vertices[i * 3 + 1].Pos = p1; + vertices[i * 3 + 1].Color = starcolor; + vertices[i * 3 + 2].Pos = p2; + vertices[i * 3 + 2].Color = starcolor; + } + driver->drawIndexedTriangleList(vertices, m_star_params.count * 3, + indices, m_star_params.count); + delete[] indices; + delete[] vertices; +#else + u16 *indices = new u16[m_star_params.count * 4]; + video::S3DVertex *vertices = + new video::S3DVertex[m_star_params.count * 4]; + for (u32 i = 0; i < m_star_params.count; i++) { + indices[i * 4 + 0] = i * 4 + 0; + indices[i * 4 + 1] = i * 4 + 1; + indices[i * 4 + 2] = i * 4 + 2; + indices[i * 4 + 3] = i * 4 + 3; + v3f r = m_stars[i]; + core::CMatrix4<f32> a; + a.buildRotateFromTo(v3f(0, 1, 0), r); + v3f p = v3f(-d, 1, -d); + v3f p1 = v3f(d, 1, -d); + v3f p2 = v3f(d, 1, d); + v3f p3 = v3f(-d, 1, d); + a.rotateVect(p); + a.rotateVect(p1); + a.rotateVect(p2); + a.rotateVect(p3); + p.rotateXYBy(wicked_time_of_day * 360 - 90); + p1.rotateXYBy(wicked_time_of_day * 360 - 90); + p2.rotateXYBy(wicked_time_of_day * 360 - 90); + p3.rotateXYBy(wicked_time_of_day * 360 - 90); + vertices[i * 4 + 0].Pos = p; + vertices[i * 4 + 0].Color = starcolor; + vertices[i * 4 + 1].Pos = p1; + vertices[i * 4 + 1].Color = starcolor; + vertices[i * 4 + 2].Pos = p2; + vertices[i * 4 + 2].Color = starcolor; + vertices[i * 4 + 3].Pos = p3; + vertices[i * 4 + 3].Color = starcolor; + } + driver->drawVertexPrimitiveList(vertices, m_star_params.count * 4, + indices, m_star_params.count, video::EVT_STANDARD, + scene::EPT_QUADS, video::EIT_16BIT); + delete[] indices; + delete[] vertices; +#endif +} 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 - */ + * 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; @@ -738,11 +815,11 @@ void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, 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 - */ + * 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 @@ -750,3 +827,151 @@ void Sky::place_sky_body( vertex.Pos.rotateXYBy(day_position); } } + +void Sky::setSunTexture(std::string sun_texture, + std::string sun_tonemap, ITextureSource *tsrc) +{ + // Ignore matching textures (with modifiers) entirely, + // but lets at least update the tonemap before hand. + m_sun_params.tonemap = sun_tonemap; + m_sun_tonemap = tsrc->isKnownSourceImage(m_sun_params.tonemap) ? + tsrc->getTexture(m_sun_params.tonemap) : NULL; + m_materials[3].Lighting = !!m_sun_tonemap; + + if (m_sun_params.texture == sun_texture) + return; + m_sun_params.texture = sun_texture; + + if (sun_texture != "") { + // We want to ensure the texture exists first. + m_sun_texture = tsrc->getTextureForMesh(m_sun_params.texture); + + if (m_sun_texture) { + m_materials[3] = m_materials[0]; + m_materials[3].setTexture(0, m_sun_texture); + m_materials[3].MaterialType = video:: + EMT_TRANSPARENT_ALPHA_CHANNEL; + // Disables texture filtering + m_materials[3].setFlag( + video::E_MATERIAL_FLAG::EMF_BILINEAR_FILTER, false); + m_materials[3].setFlag( + video::E_MATERIAL_FLAG::EMF_TRILINEAR_FILTER, false); + m_materials[3].setFlag( + video::E_MATERIAL_FLAG::EMF_ANISOTROPIC_FILTER, false); + } + } else { + m_sun_texture = nullptr; + } +} + +void Sky::setSunriseTexture(std::string sunglow_texture, + ITextureSource* tsrc) +{ + // Ignore matching textures (with modifiers) entirely. + if (m_sun_params.sunrise == sunglow_texture) + return; + m_sun_params.sunrise = sunglow_texture; + m_materials[2].setTexture(0, tsrc->getTextureForMesh( + sunglow_texture.empty() ? "sunrisebg.png" : sunglow_texture) + ); +} + +void Sky::setMoonTexture(std::string moon_texture, + std::string moon_tonemap, ITextureSource *tsrc) +{ + // Ignore matching textures (with modifiers) entirely, + // but lets at least update the tonemap before hand. + m_moon_params.tonemap = moon_tonemap; + m_moon_tonemap = tsrc->isKnownSourceImage(m_moon_params.tonemap) ? + tsrc->getTexture(m_moon_params.tonemap) : NULL; + m_materials[4].Lighting = !!m_moon_tonemap; + + if (m_moon_params.texture == moon_texture) + return; + m_moon_params.texture = moon_texture; + + if (moon_texture != "") { + // We want to ensure the texture exists first. + m_moon_texture = tsrc->getTextureForMesh(m_moon_params.texture); + + if (m_moon_texture) { + m_materials[4] = m_materials[0]; + m_materials[4].setTexture(0, m_moon_texture); + m_materials[4].MaterialType = video:: + EMT_TRANSPARENT_ALPHA_CHANNEL; + // Disables texture filtering + m_materials[4].setFlag( + video::E_MATERIAL_FLAG::EMF_BILINEAR_FILTER, false); + m_materials[4].setFlag( + video::E_MATERIAL_FLAG::EMF_TRILINEAR_FILTER, false); + m_materials[4].setFlag( + video::E_MATERIAL_FLAG::EMF_ANISOTROPIC_FILTER, false); + } + } else { + m_moon_texture = nullptr; + } +} + +void Sky::setStarCount(u16 star_count, bool force_update) +{ + // Allow force updating star count at game init. + if (m_star_params.count != star_count || force_update) { + m_star_params.count = star_count; + m_stars.clear(); + // Rebuild the stars surrounding the camera + for (u16 i = 0; i < star_count; i++) { + v3f star = v3f( + myrand_range(-10000, 10000), + myrand_range(-10000, 10000), + myrand_range(-10000, 10000) + ); + + star.normalize(); + m_stars.emplace_back(star); + } + } +} + +void Sky::setSkyColors(const SkyboxParams sky) +{ + m_sky_params.sky_color = sky.sky_color; +} + +void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, + std::string use_sun_tint) +{ + // Change sun and moon tinting: + m_sky_params.sun_tint = sun_tint; + m_sky_params.moon_tint = moon_tint; + // Faster than comparing strings every rendering frame + if (use_sun_tint == "default") + m_default_tint = true; + else if (use_sun_tint == "custom") + m_default_tint = false; + else + m_default_tint = true; +} + +void Sky::addTextureToSkybox(std::string texture, int material_id, + ITextureSource *tsrc) +{ + // Sanity check for more than six textures. + if (material_id + 5 >= SKY_MATERIAL_COUNT) + return; + // Keep a list of texture names handy. + m_sky_params.textures.emplace_back(texture); + video::ITexture *result = tsrc->getTextureForMesh(texture); + m_materials[material_id+5] = m_materials[0]; + m_materials[material_id+5].setTexture(0, result); + m_materials[material_id+5].MaterialType = video::EMT_SOLID; +} + +// To be called once at game init to setup default values. +void Sky::setSkyDefaults() +{ + SkyboxDefaults sky_defaults; + m_sky_params.sky_color = sky_defaults.getSkyColorDefaults(); + m_sun_params = sky_defaults.getSunDefaults(); + m_moon_params = sky_defaults.getMoonDefaults(); + m_star_params = sky_defaults.getStarDefaults(); +} diff --git a/src/client/sky.h b/src/client/sky.h index 9cff20e08..8637f96d4 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -21,11 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <array> #include "camera.h" #include "irrlichttypes_extrabloated.h" +#include "skyparams.h" #pragma once -#define SKY_MATERIAL_COUNT 5 -#define SKY_STAR_COUNT 200 +#define SKY_MATERIAL_COUNT 12 class ITextureSource; @@ -45,8 +45,6 @@ public: // Used by Irrlicht for optimizing rendering virtual video::SMaterial &getMaterial(u32 i) { return m_materials[i]; } - - // Used by Irrlicht for optimizing rendering virtual u32 getMaterialCount() const { return SKY_MATERIAL_COUNT; } void update(float m_time_of_day, float time_brightness, float direct_brightness, @@ -64,6 +62,23 @@ public: return m_visible ? m_skycolor : m_fallback_bg_color; } + void setSunVisible(bool sun_visible) { m_sun_params.visible = sun_visible; } + void setSunTexture(std::string sun_texture, + std::string sun_tonemap, ITextureSource *tsrc); + void setSunScale(f32 sun_scale) { m_sun_params.scale = sun_scale; } + void setSunriseVisible(bool glow_visible) { m_sun_params.sunrise_visible = glow_visible; } + void setSunriseTexture(std::string sunglow_texture, ITextureSource* tsrc); + + void setMoonVisible(bool moon_visible) { m_moon_params.visible = moon_visible; } + void setMoonTexture(std::string moon_texture, + std::string moon_tonemap, ITextureSource *tsrc); + void setMoonScale(f32 moon_scale) { m_moon_params.scale = moon_scale; } + + void setStarsVisible(bool stars_visible) { m_star_params.visible = stars_visible; } + void setStarCount(u16 star_count, bool force_update); + void setStarColor(video::SColor star_color) { m_star_params.starcolor = star_color; } + void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; } + bool getCloudsVisible() const { return m_clouds_visible && m_clouds_enabled; } const video::SColorf &getCloudColor() const { return m_cloudcolor_f; } @@ -79,12 +94,16 @@ public: m_bgcolor = bgcolor; m_skycolor = skycolor; } - void setBodiesVisible(bool visible) { m_bodies_visible = visible; } - + void setSkyColors(const SkyboxParams sky); + void setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, + std::string use_sun_tint); + void setInClouds(bool clouds) { m_in_clouds = clouds; } + void clearSkyboxTextures() { m_sky_params.textures.clear(); } + void addTextureToSkybox(std::string texture, int material_id, + ITextureSource *tsrc); private: aabb3f m_box; video::SMaterial m_materials[SKY_MATERIAL_COUNT]; - // How much sun & moon transition should affect horizon color float m_horizon_blend() { @@ -134,25 +153,46 @@ private: bool m_clouds_visible; // Whether clouds are disabled due to player underground bool m_clouds_enabled = true; // Initialised to true, reset only by set_sky API bool m_directional_colored_fog; - bool m_bodies_visible = true; // sun, moon, stars + bool m_in_clouds = true; // Prevent duplicating bools to remember old values + video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_cloudcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColor m_bgcolor; video::SColor m_skycolor; video::SColorf m_cloudcolor_f; - v3f m_stars[SKY_STAR_COUNT]; + + // pure white: becomes "diffuse light component" for clouds + video::SColorf m_cloudcolor_day_f = video::SColorf(1, 1, 1, 1); + // dawn-factoring version of pure white (note: R is above 1.0) + video::SColorf m_cloudcolor_dawn_f = video::SColorf( + 255.0f/240.0f, + 223.0f/240.0f, + 191.0f/255.0f + ); + + SkyboxParams m_sky_params; + SunParams m_sun_params; + MoonParams m_moon_params; + StarParams m_star_params; + + bool m_default_tint = true; + + std::vector<v3f> m_stars; + video::ITexture *m_sun_texture; 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); + 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); + 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); + float pos_1, float pos_2, const video::SColor &c); + void draw_stars(video::IVideoDriver *driver, float wicked_time_of_day); + void place_sky_body(std::array<video::S3DVertex, 4> &vertices, + float horizon_position, float day_position); + void setSkyDefaults(); }; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 3d9e2470a..3189ab28c 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1812,6 +1812,24 @@ bool TextureSource::generateImagePart(std::string part_of_name, } /* + Calculate the color of a single pixel drawn on top of another pixel. + + This is a little more complicated than just video::SColor::getInterpolated + because getInterpolated does not handle alpha correctly. For example, a + pixel with alpha=64 drawn atop a pixel with alpha=128 should yield a + pixel with alpha=160, while getInterpolated would yield alpha=96. +*/ +static inline video::SColor blitPixel(const video::SColor &src_c, const video::SColor &dst_c, u32 ratio) +{ + if (dst_c.getAlpha() == 0) + return src_c; + video::SColor out_c = src_c.getInterpolated(dst_c, (float)ratio / 255.0f); + out_c.setAlpha(dst_c.getAlpha() + (255 - dst_c.getAlpha()) * + src_c.getAlpha() * ratio / (255 * 255)); + return out_c; +} + +/* Draw an image on top of an another one, using the alpha channel of the source image @@ -1830,7 +1848,7 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst, s32 dst_y = dst_pos.Y + y0; video::SColor src_c = src->getPixel(src_x, src_y); video::SColor dst_c = dst->getPixel(dst_x, dst_y); - dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst_c = blitPixel(src_c, dst_c, src_c.getAlpha()); dst->setPixel(dst_x, dst_y, dst_c); } } @@ -1853,7 +1871,7 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, video::SColor dst_c = dst->getPixel(dst_x, dst_y); if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) { - dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + dst_c = blitPixel(src_c, dst_c, src_c.getAlpha()); dst->setPixel(dst_x, dst_y, dst_c); } } |