aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/camera.cpp131
-rw-r--r--src/client/camera.h21
-rw-r--r--src/client/client.cpp99
-rw-r--r--src/client/client.h31
-rw-r--r--src/client/clientenvironment.cpp35
-rw-r--r--src/client/clientevent.h46
-rw-r--r--src/client/clientlauncher.cpp2
-rw-r--r--src/client/clientmedia.cpp10
-rw-r--r--src/client/clientmedia.h5
-rw-r--r--src/client/clientobject.h7
-rw-r--r--src/client/clientsimpleobject.h35
-rw-r--r--src/client/content_cao.cpp360
-rw-r--r--src/client/content_cao.h20
-rw-r--r--src/client/content_mapblock.cpp70
-rw-r--r--src/client/filecache.cpp8
-rw-r--r--src/client/filecache.h1
-rw-r--r--src/client/fontengine.cpp2
-rw-r--r--src/client/game.cpp184
-rw-r--r--src/client/gameui.cpp27
-rw-r--r--src/client/hud.cpp269
-rw-r--r--src/client/hud.h11
-rw-r--r--src/client/imagefilters.cpp10
-rw-r--r--src/client/localplayer.cpp8
-rw-r--r--src/client/localplayer.h5
-rw-r--r--src/client/mapblock_mesh.cpp93
-rw-r--r--src/client/mesh.cpp20
-rw-r--r--src/client/mesh.h6
-rw-r--r--src/client/particles.cpp390
-rw-r--r--src/client/particles.h60
-rw-r--r--src/client/render/factory.h2
-rw-r--r--src/client/renderingengine.cpp104
-rw-r--r--src/client/shader.cpp6
-rw-r--r--src/client/sky.cpp49
-rw-r--r--src/client/sky.h2
-rw-r--r--src/client/sound_openal.cpp49
-rw-r--r--src/client/tile.cpp21
-rw-r--r--src/client/tile.h2
-rw-r--r--src/client/wieldmesh.cpp23
-rw-r--r--src/client/wieldmesh.h2
39 files changed, 1344 insertions, 882 deletions
diff --git a/src/client/camera.cpp b/src/client/camera.cpp
index 871ea709d..abc55e4b7 100644
--- a/src/client/camera.cpp
+++ b/src/client/camera.cpp
@@ -86,6 +86,51 @@ Camera::~Camera()
m_wieldmgr->drop();
}
+void Camera::notifyFovChange()
+{
+ LocalPlayer *player = m_client->getEnv().getLocalPlayer();
+ assert(player);
+
+ PlayerFovSpec spec = player->getFov();
+
+ /*
+ * Update m_old_fov_degrees first - it serves as the starting point of the
+ * upcoming transition.
+ *
+ * If an FOV transition is already active, mark current FOV as the start of
+ * the new transition. If not, set it to the previous transition's target FOV.
+ */
+ if (m_fov_transition_active)
+ m_old_fov_degrees = m_curr_fov_degrees;
+ else
+ m_old_fov_degrees = m_server_sent_fov ? m_target_fov_degrees : m_cache_fov;
+
+ /*
+ * Update m_server_sent_fov next - it corresponds to the target FOV of the
+ * upcoming transition.
+ *
+ * Set it to m_cache_fov, if server-sent FOV is 0. Otherwise check if
+ * server-sent FOV is a multiplier, and multiply it with m_cache_fov instead
+ * of overriding.
+ */
+ if (spec.fov == 0.0f) {
+ m_server_sent_fov = false;
+ m_target_fov_degrees = m_cache_fov;
+ } else {
+ m_server_sent_fov = true;
+ m_target_fov_degrees = spec.is_multiplier ? m_cache_fov * spec.fov : spec.fov;
+ }
+
+ if (spec.transition_time > 0.0f)
+ m_fov_transition_active = true;
+
+ // If FOV smooth transition is active, initialize required variables
+ if (m_fov_transition_active) {
+ m_transition_time = spec.transition_time;
+ m_fov_diff = m_target_fov_degrees - m_old_fov_degrees;
+ }
+}
+
bool Camera::successfullyCreated(std::string &error_message)
{
if (!m_playernode) {
@@ -297,9 +342,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
if (player->getParent())
player_position = player->getParent()->getPosition();
- if(player->touching_ground &&
- player_position.Y > old_player_position.Y)
- {
+ // Smooth the camera movement when the player instantly moves upward due to stepheight.
+ // To smooth the 'not touching_ground' stepheight, smoothing is necessary when jumping
+ // or swimming (for when moving from liquid to land).
+ // Disable smoothing if climbing or flying, to avoid upwards offset of player model
+ // when seen in 3rd person view.
+ bool flying = g_settings->getBool("free_move") && m_client->checkLocalPrivilege("fly");
+ if (player_position.Y > old_player_position.Y && !player->is_climbing && !flying) {
f32 oldy = old_player_position.Y;
f32 newy = player_position.Y;
f32 t = std::exp(-23 * frametime);
@@ -333,17 +382,21 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
fall_bobbing *= m_cache_fall_bobbing_amount;
}
- // Calculate players eye offset for different camera modes
- v3f PlayerEyeOffset = player->getEyeOffset();
- if (m_camera_mode == CAMERA_MODE_FIRST)
- PlayerEyeOffset += player->eye_offset_first;
- else
- PlayerEyeOffset += player->eye_offset_third;
-
- // Set head node transformation
- m_headnode->setPosition(PlayerEyeOffset+v3f(0,cameratilt*-player->hurt_tilt_strength+fall_bobbing,0));
- m_headnode->setRotation(v3f(player->getPitch(), 0, cameratilt*player->hurt_tilt_strength));
- m_headnode->updateAbsolutePosition();
+ // Calculate and translate the head SceneNode offsets
+ {
+ v3f eye_offset = player->getEyeOffset();
+ if (m_camera_mode == CAMERA_MODE_FIRST)
+ eye_offset += player->eye_offset_first;
+ else
+ eye_offset += player->eye_offset_third;
+
+ // Set head node transformation
+ eye_offset.Y += cameratilt * -player->hurt_tilt_strength + fall_bobbing;
+ m_headnode->setPosition(eye_offset);
+ m_headnode->setRotation(v3f(player->getPitch(), 0,
+ cameratilt * player->hurt_tilt_strength));
+ m_headnode->updateAbsolutePosition();
+ }
// Compute relative camera position and target
v3f rel_cam_pos = v3f(0,0,0);
@@ -458,35 +511,40 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
m_camera_position = my_cp;
/*
- * Apply server-sent FOV. If server doesn't enforce FOV,
- * check for zoom and set to zoom FOV.
- * Otherwise, default to m_cache_fov
+ * Apply server-sent FOV, instantaneous or smooth transition.
+ * If not, check for zoom and set to zoom FOV.
+ * Otherwise, default to m_cache_fov.
*/
-
- f32 fov_degrees;
- PlayerFovSpec fov_spec = player->getFov();
- if (fov_spec.fov > 0.0f) {
- // If server-sent FOV is a multiplier, multiply
- // it with m_cache_fov instead of overriding
- if (fov_spec.is_multiplier)
- fov_degrees = m_cache_fov * fov_spec.fov;
- else
- fov_degrees = fov_spec.fov;
+ if (m_fov_transition_active) {
+ // Smooth FOV transition
+ // Dynamically calculate FOV delta based on frametimes
+ f32 delta = (frametime / m_transition_time) * m_fov_diff;
+ m_curr_fov_degrees += delta;
+
+ // Mark transition as complete if target FOV has been reached
+ if ((m_fov_diff > 0.0f && m_curr_fov_degrees >= m_target_fov_degrees) ||
+ (m_fov_diff < 0.0f && m_curr_fov_degrees <= m_target_fov_degrees)) {
+ m_fov_transition_active = false;
+ m_curr_fov_degrees = m_target_fov_degrees;
+ }
+ } else if (m_server_sent_fov) {
+ // Instantaneous FOV change
+ m_curr_fov_degrees = m_target_fov_degrees;
} else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) {
// Player requests zoom, apply zoom FOV
- fov_degrees = player->getZoomFOV();
+ m_curr_fov_degrees = player->getZoomFOV();
} else {
// Set to client's selected FOV
- fov_degrees = m_cache_fov;
+ m_curr_fov_degrees = m_cache_fov;
}
- fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f);
+ m_curr_fov_degrees = rangelim(m_curr_fov_degrees, 1.0f, 160.0f);
// FOV and aspect ratio
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
m_aspect = (f32) window_size.X / (f32) window_size.Y;
- m_fov_y = fov_degrees * M_PI / 180.0;
+ m_fov_y = m_curr_fov_degrees * M_PI / 180.0;
// Increase vertical FOV on lower aspect ratios (<16:10)
- m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect)));
+ m_fov_y *= core::clamp(sqrt(16./10. / m_aspect), 1.0, 1.4);
m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y));
m_cameranode->setAspectRatio(m_aspect);
m_cameranode->setFOV(m_fov_y);
@@ -538,7 +596,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
m_wieldnode->setPosition(wield_position);
m_wieldnode->setRotation(wield_rotation);
- m_wieldnode->setColor(player->light_color);
+ m_wieldnode->setNodeLightColor(player->light_color);
// Set render distance
updateViewingRange();
@@ -553,14 +611,11 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
const bool walking = movement_XZ && player->touching_ground;
const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
const bool climbing = movement_Y && player->is_climbing;
- if ((walking || swimming || climbing) &&
- (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) {
+ if ((walking || swimming || climbing) && !flying) {
// Start animation
m_view_bobbing_state = 1;
m_view_bobbing_speed = MYMIN(speed.getLength(), 70);
- }
- else if (m_view_bobbing_state == 1)
- {
+ } else if (m_view_bobbing_state == 1) {
// Stop animation
m_view_bobbing_state = 2;
m_view_bobbing_speed = 60;
diff --git a/src/client/camera.h b/src/client/camera.h
index 88de3570a..3a59637bc 100644
--- a/src/client/camera.h
+++ b/src/client/camera.h
@@ -75,6 +75,12 @@ public:
return m_camera_position;
}
+ // Returns the absolute position of the head SceneNode in the world
+ inline v3f getHeadPosition() const
+ {
+ return m_headnode->getAbsolutePosition();
+ }
+
// Get the camera direction (in absolute camera coordinates).
// This has view bobbing applied.
inline v3f getDirection() const
@@ -106,6 +112,9 @@ public:
return MYMAX(m_fov_x, m_fov_y);
}
+ // Notify about new server-sent FOV and initialize smooth FOV transition
+ void notifyFovChange();
+
// Checks if the constructor was able to create the scene nodes
bool successfullyCreated(std::string &error_message);
@@ -180,6 +189,9 @@ private:
Client *m_client;
+ // Default Client FOV (as defined by the "fov" setting)
+ f32 m_cache_fov;
+
// Absolute camera position
v3f m_camera_position;
// Absolute camera direction
@@ -187,6 +199,14 @@ private:
// Camera offset
v3s16 m_camera_offset;
+ // Server-sent FOV variables
+ bool m_server_sent_fov = false;
+ f32 m_curr_fov_degrees, m_old_fov_degrees, m_target_fov_degrees;
+
+ // FOV transition variables
+ bool m_fov_transition_active = false;
+ f32 m_fov_diff, m_transition_time;
+
v2f m_wieldmesh_offset = v2f(55.0f, -35.0f);
v2f m_arm_dir;
v2f m_cam_vel;
@@ -224,7 +244,6 @@ private:
f32 m_cache_fall_bobbing_amount;
f32 m_cache_view_bobbing_amount;
- f32 m_cache_fov;
bool m_arm_inertia;
std::list<Nametag *> m_nametags;
diff --git a/src/client/client.cpp b/src/client/client.cpp
index c6d28ce80..65e5b3d8c 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -61,6 +61,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
extern gui::IGUIEnvironment* guienv;
/*
+ Utility classes
+*/
+
+u32 PacketCounter::sum() const
+{
+ u32 n = 0;
+ for (const auto &it : m_packets)
+ n += it.second;
+ return n;
+}
+
+void PacketCounter::print(std::ostream &o) const
+{
+ for (const auto &it : m_packets) {
+ auto name = it.first >= TOCLIENT_NUM_MSG_TYPES ? "?"
+ : toClientCommandTable[it.first].name;
+ o << "cmd " << it.first << " (" << name << ") count "
+ << it.second << std::endl;
+ }
+}
+
+/*
Client
*/
@@ -164,7 +186,7 @@ void Client::loadMods()
infostream << mod.name << " ";
infostream << std::endl;
- // Load and run "mod" scripts
+ // Load "mod" scripts
for (const ModSpec &mod : m_mods) {
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
throw ModError("Error loading mod \"" + mod.name +
@@ -174,7 +196,7 @@ void Client::loadMods()
scanModIntoMemory(mod.name, mod.path);
}
- // Load and run "mod" scripts
+ // Run them
for (const ModSpec &mod : m_mods)
m_script->loadModFromMemory(mod.name);
@@ -183,10 +205,14 @@ void Client::loadMods()
// Run a callback when mods are loaded
m_script->on_mods_loaded();
+
+ // Create objects if they're ready
if (m_state == LC_Ready)
m_script->on_client_ready(m_env.getLocalPlayer());
if (m_camera)
m_script->on_camera_ready(m_camera);
+ if (m_minimap)
+ m_script->on_minimap_ready(m_minimap);
}
bool Client::checkBuiltinIntegrity()
@@ -336,12 +362,14 @@ void Client::step(float dtime)
{
float &counter = m_packetcounter_timer;
counter -= dtime;
- if(counter <= 0.0)
+ if(counter <= 0.0f)
{
- counter = 20.0;
+ counter = 30.0f;
+ u32 sum = m_packetcounter.sum();
+ float avg = sum / counter;
- infostream << "Client packetcounter (" << m_packetcounter_timer
- << "):"<<std::endl;
+ infostream << "Client packetcounter (" << counter << "s): "
+ << "sum=" << sum << " avg=" << avg << "/s" << std::endl;
m_packetcounter.print(infostream);
m_packetcounter.clear();
}
@@ -431,12 +459,9 @@ void Client::step(float dtime)
/*
Handle environment
*/
- // Control local player (0ms)
LocalPlayer *player = m_env.getLocalPlayer();
- assert(player);
- player->applyControl(dtime, &m_env);
- // Step environment
+ // Step environment (also handles player controls)
m_env.step(dtime);
m_sound->step(dtime);
@@ -621,14 +646,17 @@ void Client::step(float dtime)
m_mod_storage_save_timer -= dtime;
if (m_mod_storage_save_timer <= 0.0f) {
- verbosestream << "Saving registered mod storages." << std::endl;
m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
+ int n = 0;
for (std::unordered_map<std::string, ModMetadata *>::const_iterator
it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
if (it->second->isModified()) {
it->second->save(getModStoragePath());
+ n++;
}
}
+ if (n > 0)
+ infostream << "Saved " << n << " modified mod storages." << std::endl;
}
// Write server map
@@ -639,11 +667,9 @@ void Client::step(float dtime)
}
}
-bool Client::loadMedia(const std::string &data, const std::string &filename)
+bool Client::loadMedia(const std::string &data, const std::string &filename,
+ bool from_media_push)
{
- // Silly irrlicht's const-incorrectness
- Buffer<char> data_rw(data.c_str(), data.size());
-
std::string name;
const char *image_ext[] = {
@@ -653,12 +679,15 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
};
name = removeStringEnd(filename, image_ext);
if (!name.empty()) {
- verbosestream<<"Client: Attempting to load image "
- <<"file \""<<filename<<"\""<<std::endl;
+ TRACESTREAM(<< "Client: Attempting to load image "
+ << "file \"" << filename << "\"" << std::endl);
io::IFileSystem *irrfs = RenderingEngine::get_filesystem();
video::IVideoDriver *vdrv = RenderingEngine::get_video_driver();
+ // Silly irrlicht's const-incorrectness
+ Buffer<char> data_rw(data.c_str(), data.size());
+
// Create an irrlicht memory file
io::IReadFile *rfile = irrfs->createMemoryReadFile(
*data_rw, data_rw.getSize(), "_tempreadfile");
@@ -687,17 +716,15 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
};
name = removeStringEnd(filename, sound_ext);
if (!name.empty()) {
- verbosestream<<"Client: Attempting to load sound "
- <<"file \""<<filename<<"\""<<std::endl;
- m_sound->loadSoundData(name, data);
- return true;
+ TRACESTREAM(<< "Client: Attempting to load sound "
+ << "file \"" << filename << "\"" << std::endl);
+ return m_sound->loadSoundData(name, data);
}
const char *model_ext[] = {
".x", ".b3d", ".md2", ".obj",
NULL
};
-
name = removeStringEnd(filename, model_ext);
if (!name.empty()) {
verbosestream<<"Client: Storing model into memory: "
@@ -714,9 +741,11 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
};
name = removeStringEnd(filename, translate_ext);
if (!name.empty()) {
- verbosestream << "Client: Loading translation: "
- << "\"" << filename << "\"" << std::endl;
- g_translations->loadTranslation(data);
+ if (from_media_push)
+ return false;
+ TRACESTREAM(<< "Client: Loading translation: "
+ << "\"" << filename << "\"" << std::endl);
+ g_client_translations->loadTranslation(data);
return true;
}
@@ -1722,8 +1751,11 @@ void Client::afterContentReceived()
text = wgettext("Initializing nodes...");
RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 72);
m_nodedef->updateAliases(m_itemdef);
- for (const auto &path : getTextureDirs())
- m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
+ for (const auto &path : getTextureDirs()) {
+ TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
+ m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
+ m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
+ }
m_nodedef->setNodeRegistrationStatus(true);
m_nodedef->runNodeResolveCallbacks();
delete[] text;
@@ -1780,13 +1812,24 @@ void Client::makeScreenshot()
char timetstamp_c[64];
strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
- std::string filename_base = g_settings->get("screenshot_path")
+ std::string screenshot_dir;
+
+ if (fs::IsPathAbsolute(g_settings->get("screenshot_path")))
+ screenshot_dir = g_settings->get("screenshot_path");
+ else
+ screenshot_dir = porting::path_user + DIR_DELIM + g_settings->get("screenshot_path");
+
+ std::string filename_base = screenshot_dir
+ DIR_DELIM
+ std::string("screenshot_")
+ std::string(timetstamp_c);
std::string filename_ext = "." + g_settings->get("screenshot_format");
std::string filename;
+ // Create the directory if it doesn't already exist.
+ // Otherwise, saving the screenshot would fail.
+ fs::CreateDir(screenshot_dir);
+
u32 quality = (u32)g_settings->getS32("screenshot_quality");
quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255;
diff --git a/src/client/client.h b/src/client/client.h
index 1291b944c..733634db1 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -82,34 +82,24 @@ public:
void add(u16 command)
{
- std::map<u16, u16>::iterator n = m_packets.find(command);
- if(n == m_packets.end())
- {
+ auto n = m_packets.find(command);
+ if (n == m_packets.end())
m_packets[command] = 1;
- }
else
- {
n->second++;
- }
}
void clear()
{
- for (auto &m_packet : m_packets) {
- m_packet.second = 0;
- }
+ m_packets.clear();
}
- void print(std::ostream &o)
- {
- for (const auto &m_packet : m_packets) {
- o << "cmd "<< m_packet.first <<" count "<< m_packet.second << std::endl;
- }
- }
+ u32 sum() const;
+ void print(std::ostream &o) const;
private:
// command, count
- std::map<u16, u16> m_packets;
+ std::map<u16, u32> m_packets;
};
class ClientScripting;
@@ -232,6 +222,7 @@ public:
void handleCommand_FormspecPrepend(NetworkPacket *pkt);
void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt);
void handleCommand_PlayerSpeed(NetworkPacket *pkt);
+ void handleCommand_MediaPush(NetworkPacket *pkt);
void ProcessData(NetworkPacket *pkt);
@@ -386,7 +377,8 @@ public:
// The following set of functions is used by ClientMediaDownloader
// Insert a media file appropriately into the appropriate manager
- bool loadMedia(const std::string &data, const std::string &filename);
+ bool loadMedia(const std::string &data, const std::string &filename,
+ bool from_media_push = false);
// Send a request for conventional media transfer
void request_media(const std::vector<std::string> &file_requests);
@@ -498,6 +490,7 @@ private:
Camera *m_camera = nullptr;
Minimap *m_minimap = nullptr;
bool m_minimap_disabled_by_server = false;
+
// Server serialization version
u8 m_server_ser_ver;
@@ -539,7 +532,6 @@ private:
AuthMechanism m_chosen_auth_mech;
void *m_auth_data = nullptr;
-
bool m_access_denied = false;
bool m_access_denied_reconnect = false;
std::string m_access_denied_reason = "";
@@ -548,7 +540,10 @@ private:
bool m_nodedef_received = false;
bool m_activeobjects_received = false;
bool m_mods_loaded = false;
+
ClientMediaDownloader *m_media_downloader;
+ // Set of media filenames pushed by server at runtime
+ std::unordered_set<std::string> m_media_pushed_files;
// time_of_day speed approximation for old protocol
bool m_time_of_day_set = false;
diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp
index 52d133781..895b0193c 100644
--- a/src/client/clientenvironment.cpp
+++ b/src/client/clientenvironment.cpp
@@ -216,6 +216,9 @@ void ClientEnvironment::step(float dtime)
*/
{
+ // Control local player
+ lplayer->applyControl(dtime_part, this);
+
// Apply physics
if (!free_move && !is_climbing) {
// Gravity
@@ -320,21 +323,8 @@ void ClientEnvironment::step(float dtime)
// Step object
cao->step(dtime, this);
- if (update_lighting) {
- // Update lighting
- u8 light = 0;
- bool pos_ok;
-
- // Get node at head
- v3s16 p = cao->getLightPosition();
- MapNode n = this->m_map->getNode(p, &pos_ok);
- if (pos_ok)
- light = n.getLightBlend(day_night_ratio, m_client->ndef());
- else
- light = blend_light(day_night_ratio, LIGHT_SUN, 0);
-
- cao->updateLight(light);
- }
+ if (update_lighting)
+ cao->updateLight(day_night_ratio);
};
m_ao_manager.step(dtime, cb_state);
@@ -402,18 +392,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
object->addToScene(m_texturesource);
// Update lighting immediately
- u8 light = 0;
- bool pos_ok;
-
- // Get node at head
- v3s16 p = object->getLightPosition();
- MapNode n = m_map->getNode(p, &pos_ok);
- if (pos_ok)
- light = n.getLightBlend(getDayNightRatio(), m_client->ndef());
- else
- light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
-
- object->updateLight(light);
+ object->updateLight(getDayNightRatio());
return object->getId();
}
@@ -450,7 +429,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
// Object initialized:
if ((obj = getActiveObject(new_id))) {
// Final step is to update all children which are already known
- // Data provided by GENERIC_CMD_SPAWN_INFANT
+ // Data provided by AO_CMD_SPAWN_INFANT
const auto &children = obj->getAttachmentChildIds();
for (auto c_id : children) {
if (auto *o = getActiveObject(c_id))
diff --git a/src/client/clientevent.h b/src/client/clientevent.h
index f5689c25b..9bd31efce 100644
--- a/src/client/clientevent.h
+++ b/src/client/clientevent.h
@@ -21,8 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include "irrlichttypes_bloated.h"
-#include "hud.h"
-#include "skyparams.h"
+
+struct ParticleParameters;
+struct ParticleSpawnerParameters;
+struct SkyboxParams;
+struct SunParams;
+struct MoonParams;
+struct StarParams;
enum ClientEventType : u8
{
@@ -77,44 +82,12 @@ struct ClientEvent
} show_formspec;
// struct{
//} textures_updated;
+ ParticleParameters *spawn_particle;
struct
{
- v3f *pos;
- v3f *vel;
- v3f *acc;
- f32 expirationtime;
- f32 size;
- bool collisiondetection;
- bool collision_removal;
- bool object_collision;
- bool vertical;
- std::string *texture;
- struct TileAnimationParams animation;
- u8 glow;
- } spawn_particle;
- struct
- {
- u16 amount;
- f32 spawntime;
- v3f *minpos;
- v3f *maxpos;
- v3f *minvel;
- v3f *maxvel;
- v3f *minacc;
- v3f *maxacc;
- f32 minexptime;
- f32 maxexptime;
- f32 minsize;
- f32 maxsize;
- bool collisiondetection;
- bool collision_removal;
- bool object_collision;
+ ParticleSpawnerParameters *p;
u16 attached_id;
- bool vertical;
- std::string *texture;
u64 id;
- struct TileAnimationParams animation;
- u8 glow;
} add_particlespawner;
struct
{
@@ -136,6 +109,7 @@ struct ClientEvent
v3f *world_pos;
v2s32 *size;
s16 z_index;
+ std::string *text2;
} hudadd;
struct
{
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index 2a9d6097f..f18915a55 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -105,7 +105,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
}
RenderingEngine::get_instance()->setupTopLevelWindow(PROJECT_NAME_C);
-
+
/*
This changes the minimum allowed number of vertices in a VBO.
Default is 500.
diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp
index 6da99bbbf..8cd3b6bcc 100644
--- a/src/client/clientmedia.cpp
+++ b/src/client/clientmedia.cpp
@@ -35,6 +35,15 @@ static std::string getMediaCacheDir()
return porting::path_cache + DIR_DELIM + "media";
}
+bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &filedata)
+{
+ FileCache media_cache(getMediaCacheDir());
+ std::string sha1_hex = hex_encode(raw_hash);
+ if (!media_cache.exists(sha1_hex))
+ return media_cache.update(sha1_hex, filedata);
+ return true;
+}
+
/*
ClientMediaDownloader
*/
@@ -559,7 +568,6 @@ bool ClientMediaDownloader::checkAndLoad(
return true;
}
-
/*
Minetest Hashset File Format
diff --git a/src/client/clientmedia.h b/src/client/clientmedia.h
index 92831082c..5a918535b 100644
--- a/src/client/clientmedia.h
+++ b/src/client/clientmedia.h
@@ -33,6 +33,11 @@ struct HTTPFetchResult;
#define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS'
#define MTHASHSET_FILE_NAME "index.mth"
+// Store file into media cache (unless it exists already)
+// Validating the hash is responsibility of the caller
+bool clientMediaUpdateCache(const std::string &raw_hash,
+ const std::string &filedata);
+
class ClientMediaDownloader
{
public:
diff --git a/src/client/clientobject.h b/src/client/clientobject.h
index 12e0db35b..ecd8059ef 100644
--- a/src/client/clientobject.h
+++ b/src/client/clientobject.h
@@ -41,10 +41,9 @@ public:
virtual void addToScene(ITextureSource *tsrc) {}
virtual void removeFromScene(bool permanent) {}
- // 0 <= light_at_pos <= LIGHT_SUN
- virtual void updateLight(u8 light_at_pos) {}
- virtual void updateLightNoCheck(u8 light_at_pos) {}
- virtual v3s16 getLightPosition() { return v3s16(0, 0, 0); }
+
+ virtual void updateLight(u32 day_night_ratio) {}
+
virtual bool getCollisionBox(aabb3f *toset) const { return false; }
virtual bool getSelectionBox(aabb3f *toset) const { return false; }
virtual bool collideWithObjects() const { return false; }
diff --git a/src/client/clientsimpleobject.h b/src/client/clientsimpleobject.h
new file mode 100644
index 000000000..f4a40bcd3
--- /dev/null
+++ b/src/client/clientsimpleobject.h
@@ -0,0 +1,35 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes_bloated.h"
+class ClientEnvironment;
+
+class ClientSimpleObject
+{
+protected:
+public:
+ bool m_to_be_removed = false;
+
+ ClientSimpleObject() = default;
+ virtual ~ClientSimpleObject() = default;
+
+ virtual void step(float dtime) {}
+};
diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp
index d148df522..4f949f6b0 100644
--- a/src/client/content_cao.cpp
+++ b/src/client/content_cao.cpp
@@ -162,6 +162,15 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
matrix.setTextureScale(txs, tys);
}
+// Evaluate transform chain recursively; irrlicht does not do this for us
+static void updatePositionRecursive(scene::ISceneNode *node)
+{
+ scene::ISceneNode *parent = node->getParent();
+ if (parent)
+ updatePositionRecursive(parent);
+ node->updateAbsolutePosition();
+}
+
/*
TestCAO
*/
@@ -181,8 +190,7 @@ public:
void addToScene(ITextureSource *tsrc);
void removeFromScene(bool permanent);
- void updateLight(u8 light_at_pos);
- v3s16 getLightPosition();
+ void updateLight(u32 day_night_ratio);
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
@@ -254,13 +262,8 @@ void TestCAO::removeFromScene(bool permanent)
m_node = NULL;
}
-void TestCAO::updateLight(u8 light_at_pos)
-{
-}
-
-v3s16 TestCAO::getLightPosition()
+void TestCAO::updateLight(u32 day_night_ratio)
{
- return floatToInt(m_position, BS);
}
void TestCAO::updateNodePos()
@@ -304,7 +307,6 @@ void TestCAO::processMessage(const std::string &data)
GenericCAO
*/
-#include "genericobject.h"
#include "clientobject.h"
GenericCAO::GenericCAO(Client *client, ClientEnvironment *env):
@@ -476,6 +478,7 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit
parent->addAttachmentChild(m_id);
}
+
updateAttachments();
}
@@ -576,16 +579,23 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
m_visuals_expired = false;
- if (!m_prop.is_visible) {
+ if (!m_prop.is_visible)
return;
- }
+
+ infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl;
if (m_enable_shaders) {
IShaderSource *shader_source = m_client->getShaderSource();
- u32 shader_id = shader_source->getShader(
- "object_shader",
- TILE_MATERIAL_BASIC,
- NDT_NORMAL);
+ MaterialType material_type;
+
+ if (m_prop.shaded && m_prop.glow == 0)
+ material_type = (m_prop.use_texture_alpha) ?
+ TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC;
+ else
+ material_type = (m_prop.use_texture_alpha) ?
+ TILE_MATERIAL_PLAIN_ALPHA : TILE_MATERIAL_PLAIN;
+
+ u32 shader_id = shader_source->getShader("object_shader", material_type, NDT_NORMAL);
m_material_type = shader_source->getShaderInfo(shader_id).material;
} else {
m_material_type = (m_prop.use_texture_alpha) ?
@@ -593,7 +603,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
}
auto grabMatrixNode = [this] {
- infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl;
m_matrixnode = RenderingEngine::get_scene_manager()->
addDummyTransformationSceneNode();
m_matrixnode->grab();
@@ -714,6 +723,8 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
mesh->drop();
m_meshnode->setScale(m_prop.visual_size);
+ m_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
+ m_prop.backface_culling);
setSceneNodeMaterial(m_meshnode);
} else if (m_prop.visual == "mesh") {
@@ -724,6 +735,14 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
addAnimatedMeshSceneNode(mesh, m_matrixnode);
m_animated_meshnode->grab();
mesh->drop(); // The scene node took hold of it
+
+ if (!checkMeshNormals(mesh)) {
+ infostream << "GenericCAO: recalculating normals for mesh "
+ << m_prop.mesh << std::endl;
+ m_smgr->getMeshManipulator()->
+ recalculateNormals(mesh, true, false);
+ }
+
m_animated_meshnode->animateJoints(); // Needed for some animations
m_animated_meshnode->setScale(m_prop.visual_size);
@@ -775,15 +794,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
if (node && m_matrixnode)
node->setParent(m_matrixnode);
- if (node && !m_prop.nametag.empty() && !m_is_local_player) {
- // Add nametag
- v3f pos;
- pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f;
- m_nametag = m_client->getCamera()->addNametag(node,
- m_prop.nametag, m_prop.nametag_color,
- pos);
- }
-
+ updateNametag();
updateNodePos();
updateAnimation();
updateBonePosition();
@@ -791,34 +802,32 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
setNodeLight(m_last_light);
}
-void GenericCAO::updateLight(u8 light_at_pos)
+void GenericCAO::updateLight(u32 day_night_ratio)
{
- // Don't update light of attached one
- if (getParent() != NULL) {
+ if (m_glow < 0)
return;
- }
-
- updateLightNoCheck(light_at_pos);
- // Update light of all children
- for (u16 i : m_attachment_child_ids) {
- ClientActiveObject *obj = m_env->getActiveObject(i);
- if (obj) {
- obj->updateLightNoCheck(light_at_pos);
+ u8 light_at_pos = 0;
+ bool pos_ok = false;
+
+ v3s16 pos[3];
+ u16 npos = getLightPosition(pos);
+ for (u16 i = 0; i < npos; i++) {
+ bool this_ok;
+ MapNode n = m_env->getMap().getNode(pos[i], &this_ok);
+ if (this_ok) {
+ u8 this_light = n.getLightBlend(day_night_ratio, m_client->ndef());
+ light_at_pos = MYMAX(light_at_pos, this_light);
+ pos_ok = true;
}
}
-}
-
-void GenericCAO::updateLightNoCheck(u8 light_at_pos)
-{
- if (m_glow < 0)
- return;
+ if (!pos_ok)
+ light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0);
- u8 li = decode_light(light_at_pos + m_glow);
-
- if (li != m_last_light) {
- m_last_light = li;
- setNodeLight(li);
+ u8 light = decode_light(light_at_pos + m_glow);
+ if (light != m_last_light) {
+ m_last_light = light;
+ setNodeLight(light);
}
}
@@ -827,24 +836,26 @@ 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;
+ m_wield_meshnode->setNodeLightColor(color);
+ return;
+ }
+ if (m_enable_shaders) {
if (m_prop.visual == "upright_sprite") {
+ if (!m_meshnode)
+ return;
+
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;
+ buf->getMaterial().EmissiveColor = color;
}
} else {
+ scene::ISceneNode *node = getSceneNode();
+ if (!node)
+ return;
+
for (u32 i = 0; i < node->getMaterialCount(); ++i) {
video::SMaterial &material = node->getMaterial(i);
material.EmissiveColor = color;
@@ -861,12 +872,49 @@ void GenericCAO::setNodeLight(u8 light)
}
}
-v3s16 GenericCAO::getLightPosition()
+u16 GenericCAO::getLightPosition(v3s16 *pos)
+{
+ const auto &box = m_prop.collisionbox;
+ pos[0] = floatToInt(m_position + box.MinEdge * BS, BS);
+ pos[1] = floatToInt(m_position + box.MaxEdge * BS, BS);
+
+ // Skip center pos if it falls into the same node as Min or MaxEdge
+ if ((box.MaxEdge - box.MinEdge).getLengthSQ() < 3.0f)
+ return 2;
+ pos[2] = floatToInt(m_position + box.getCenter() * BS, BS);
+ return 3;
+}
+
+void GenericCAO::updateNametag()
{
- if (m_is_player)
- return floatToInt(m_position + v3f(0, 0.5 * BS, 0), BS);
+ if (m_is_local_player) // No nametag for local player
+ return;
+
+ if (m_prop.nametag.empty()) {
+ // Delete nametag
+ if (m_nametag) {
+ m_client->getCamera()->removeNametag(m_nametag);
+ m_nametag = nullptr;
+ }
+ return;
+ }
- return floatToInt(m_position, BS);
+ scene::ISceneNode *node = getSceneNode();
+ if (!node)
+ return;
+
+ v3f pos;
+ pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f;
+ if (!m_nametag) {
+ // Add nametag
+ m_nametag = m_client->getCamera()->addNametag(node,
+ m_prop.nametag, m_prop.nametag_color, pos);
+ } else {
+ // Update nametag
+ m_nametag->nametag_text = m_prop.nametag;
+ m_nametag->nametag_color = m_prop.nametag_color;
+ m_nametag->nametag_pos = pos;
+ }
}
void GenericCAO::updateNodePos()
@@ -1072,10 +1120,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
updateTextures(m_previous_texture_modifier);
}
}
+
if (!getParent() && std::fabs(m_prop.automatic_rotate) > 0.001) {
- m_rotation.Y += dtime * m_prop.automatic_rotate * 180 / M_PI;
- rot_translator.val_current = m_rotation;
- updateNodePos();
+ // This is the child node's rotation. It is only used for automatic_rotate.
+ v3f local_rot = node->getRotation();
+ local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG *
+ m_prop.automatic_rotate);
+ node->setRotation(local_rot);
}
if (!getParent() && m_prop.automatic_face_movement_dir &&
@@ -1096,6 +1147,18 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
rot_translator.val_current = m_rotation;
updateNodePos();
}
+
+ if (m_animated_meshnode) {
+ // Everything must be updated; the whole transform
+ // chain as well as the animated mesh node.
+ // Otherwise, bone attachments would be relative to
+ // a position that's one frame old.
+ if (m_matrixnode)
+ updatePositionRecursive(m_matrixnode);
+ m_animated_meshnode->updateAbsolutePosition();
+ m_animated_meshnode->animateJoints();
+ updateBonePosition();
+ }
}
void GenericCAO::updateTexturePos()
@@ -1360,16 +1423,53 @@ void GenericCAO::updateBonePosition()
return;
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
- for(std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
- ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
- std::string bone_name = (*ii).first;
- v3f bone_pos = (*ii).second.X;
- v3f bone_rot = (*ii).second.Y;
+ for (auto &it : m_bone_position) {
+ std::string bone_name = it.first;
irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
- if(bone)
- {
- bone->setPosition(bone_pos);
+ if (bone) {
+ bone->setPosition(it.second.X);
+ bone->setRotation(it.second.Y);
+ }
+ }
+
+ // search through bones to find mistakenly rotated bones due to bug in Irrlicht
+ for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) {
+ irr::scene::IBoneSceneNode *bone = m_animated_meshnode->getJointNode(i);
+ if (!bone)
+ continue;
+
+ //If bone is manually positioned there is no need to perform the bug check
+ bool skip = false;
+ for (auto &it : m_bone_position) {
+ if (it.first == bone->getName()) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+
+ // Workaround for Irrlicht bug
+ // We check each bone to see if it has been rotated ~180deg from its expected position due to a bug in Irricht
+ // when using EJUOR_CONTROL joint control. If the bug is detected we update the bone to the proper position
+ // and update the bones transformation.
+ v3f bone_rot = bone->getRelativeTransformation().getRotationDegrees();
+ float offset = fabsf(bone_rot.X - bone->getRotation().X);
+ if (offset > 179.9f && offset < 180.1f) {
bone->setRotation(bone_rot);
+ bone->updateAbsolutePosition();
+ }
+ }
+ // The following is needed for set_bone_pos to propagate to
+ // attached objects correctly.
+ // Irrlicht ought to do this, but doesn't when using EJUOR_CONTROL.
+ for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) {
+ auto bone = m_animated_meshnode->getJointNode(i);
+ // Look for the root bone.
+ if (bone && bone->getParent() == m_animated_meshnode) {
+ // Update entire skeleton.
+ bone->updateAbsolutePositionOfAllChildren();
+ break;
}
}
}
@@ -1394,15 +1494,17 @@ void GenericCAO::updateAttachments()
if (!parent) { // Detach or don't attach
if (m_matrixnode) {
+ v3s16 camera_offset = m_env->getCameraOffset();
v3f old_pos = getPosition();
m_matrixnode->setParent(m_smgr->getRootSceneNode());
- getPosRotMatrix().setTranslation(old_pos);
+ getPosRotMatrix().setTranslation(old_pos - intToFloat(camera_offset, BS));
m_matrixnode->updateAbsolutePosition();
}
}
else // Attach
{
+ parent->updateAttachments();
scene::ISceneNode *parent_node = parent->getSceneNode();
scene::IAnimatedMeshSceneNode *parent_animated_mesh_node =
parent->getAnimatedMeshSceneNode();
@@ -1412,6 +1514,7 @@ void GenericCAO::updateAttachments()
if (m_matrixnode && parent_node) {
m_matrixnode->setParent(parent_node);
+ parent_node->updateAbsolutePosition();
getPosRotMatrix().setTranslation(m_attachment_position);
//setPitchYawRoll(getPosRotMatrix(), m_attachment_rotation);
// use Irrlicht eulers instead
@@ -1421,21 +1524,55 @@ void GenericCAO::updateAttachments()
}
}
+bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const
+{
+ const ObjectProperties &old = m_prop;
+ /* Visuals do not need to be expired for:
+ * - nametag props: handled by updateNametag()
+ * - textures: handled by updateTextures()
+ * - sprite props: handled by updateTexturePos()
+ * - glow: handled by updateLight()
+ * - any other properties that do not change appearance
+ */
+
+ bool uses_legacy_texture = new_.wield_item.empty() &&
+ (new_.visual == "wielditem" || new_.visual == "item");
+ // Ordered to compare primitive types before std::vectors
+ return old.backface_culling != new_.backface_culling ||
+ old.is_visible != new_.is_visible ||
+ old.mesh != new_.mesh ||
+ old.shaded != new_.shaded ||
+ old.use_texture_alpha != new_.use_texture_alpha ||
+ old.visual != new_.visual ||
+ old.visual_size != new_.visual_size ||
+ old.wield_item != new_.wield_item ||
+ old.colors != new_.colors ||
+ (uses_legacy_texture && old.textures != new_.textures);
+}
+
void GenericCAO::processMessage(const std::string &data)
{
//infostream<<"GenericCAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
- if (cmd == GENERIC_CMD_SET_PROPERTIES) {
- m_prop = gob_read_set_properties(is);
+ if (cmd == AO_CMD_SET_PROPERTIES) {
+ ObjectProperties newprops;
+ newprops.deSerialize(is);
+
+ // Check what exactly changed
+ bool expire_visuals = visualExpiryRequired(newprops);
+ bool textures_changed = m_prop.textures != newprops.textures;
+
+ // Apply changes
+ m_prop = std::move(newprops);
m_selection_box = m_prop.selectionbox;
m_selection_box.MinEdge *= BS;
m_selection_box.MaxEdge *= BS;
- m_tx_size.X = 1.0 / m_prop.spritediv.X;
- m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
+ m_tx_size.X = 1.0f / m_prop.spritediv.X;
+ m_tx_size.Y = 1.0f / m_prop.spritediv.Y;
if(!m_initial_tx_basepos_set){
m_initial_tx_basepos_set = true;
@@ -1455,18 +1592,25 @@ void GenericCAO::processMessage(const std::string &data)
if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty())
m_prop.nametag = m_name;
- expireVisuals();
- } else if (cmd == GENERIC_CMD_UPDATE_POSITION) {
+ if (expire_visuals) {
+ expireVisuals();
+ } else {
+ infostream << "GenericCAO: properties updated but expiring visuals"
+ << " not necessary" << std::endl;
+ if (textures_changed) {
+ // don't update while punch texture modifier is active
+ if (m_reset_textures_timer < 0)
+ updateTextures(m_current_texture_modifier);
+ }
+ updateNametag();
+ }
+ } else if (cmd == AO_CMD_UPDATE_POSITION) {
// Not sent by the server if this object is an attachment.
// We might however get here if the server notices the object being detached before the client.
m_position = readV3F32(is);
m_velocity = readV3F32(is);
m_acceleration = readV3F32(is);
-
- if (std::fabs(m_prop.automatic_rotate) < 0.001f)
- m_rotation = readV3F32(is);
- else
- readV3F32(is);
+ m_rotation = readV3F32(is);
m_rotation = wrapDegrees_0_360_v3f(m_rotation);
bool do_interpolate = readU8(is);
@@ -1490,16 +1634,16 @@ void GenericCAO::processMessage(const std::string &data)
}
rot_translator.update(m_rotation, false, update_interval);
updateNodePos();
- } else if (cmd == GENERIC_CMD_SET_TEXTURE_MOD) {
+ } else if (cmd == AO_CMD_SET_TEXTURE_MOD) {
std::string mod = deSerializeString(is);
- // immediatly reset a engine issued texture modifier if a mod sends a different one
+ // immediately reset a engine issued texture modifier if a mod sends a different one
if (m_reset_textures_timer > 0) {
m_reset_textures_timer = -1;
updateTextures(m_previous_texture_modifier);
}
updateTextures(mod);
- } else if (cmd == GENERIC_CMD_SET_SPRITE) {
+ } else if (cmd == AO_CMD_SET_SPRITE) {
v2s16 p = readV2S16(is);
int num_frames = readU16(is);
float framelength = readF32(is);
@@ -1511,7 +1655,7 @@ void GenericCAO::processMessage(const std::string &data)
m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
updateTexturePos();
- } else if (cmd == GENERIC_CMD_SET_PHYSICS_OVERRIDE) {
+ } else if (cmd == AO_CMD_SET_PHYSICS_OVERRIDE) {
float override_speed = readF32(is);
float override_jump = readF32(is);
float override_gravity = readF32(is);
@@ -1531,7 +1675,7 @@ void GenericCAO::processMessage(const std::string &data)
player->physics_override_sneak_glitch = sneak_glitch;
player->physics_override_new_move = new_move;
}
- } else if (cmd == GENERIC_CMD_SET_ANIMATION) {
+ } else if (cmd == AO_CMD_SET_ANIMATION) {
// TODO: change frames send as v2s32 value
v2f range = readV2F32(is);
if (!m_is_local_player) {
@@ -1565,17 +1709,17 @@ void GenericCAO::processMessage(const std::string &data)
updateAnimation();
}
}
- } else if (cmd == GENERIC_CMD_SET_ANIMATION_SPEED) {
+ } else if (cmd == AO_CMD_SET_ANIMATION_SPEED) {
m_animation_speed = readF32(is);
updateAnimationSpeed();
- } else if (cmd == GENERIC_CMD_SET_BONE_POSITION) {
+ } else if (cmd == AO_CMD_SET_BONE_POSITION) {
std::string bone = deSerializeString(is);
v3f position = readV3F32(is);
v3f rotation = readV3F32(is);
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
- updateBonePosition();
- } else if (cmd == GENERIC_CMD_ATTACH_TO) {
+ // updateBonePosition(); now called every step
+ } else if (cmd == AO_CMD_ATTACH_TO) {
u16 parent_id = readS16(is);
std::string bone = deSerializeString(is);
v3f position = readV3F32(is);
@@ -1586,7 +1730,7 @@ void GenericCAO::processMessage(const std::string &data)
// localplayer itself can't be attached to localplayer
if (!m_is_local_player)
m_is_visible = !m_attached_to_local;
- } else if (cmd == GENERIC_CMD_PUNCHED) {
+ } else if (cmd == AO_CMD_PUNCHED) {
u16 result_hp = readU16(is);
// Use this instead of the send damage to not interfere with prediction
@@ -1607,13 +1751,11 @@ void GenericCAO::processMessage(const std::string &data)
m_smgr, m_env, m_position,
v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS);
m_env->addSimpleObject(simple);
- } else if (m_reset_textures_timer < 0) {
- // TODO: Execute defined fast response
- // Flashing shall suffice as there is no definition
+ } else if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) {
m_reset_textures_timer = 0.05;
if(damage >= 2)
m_reset_textures_timer += 0.05 * damage;
- updateTextures(m_current_texture_modifier + "^[brighten");
+ updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier);
}
}
@@ -1624,7 +1766,7 @@ void GenericCAO::processMessage(const std::string &data)
if (!m_is_player)
clearChildAttachments();
}
- } else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) {
+ } else if (cmd == AO_CMD_UPDATE_ARMOR_GROUPS) {
m_armor_groups.clear();
int armor_groups_size = readU16(is);
for(int i=0; i<armor_groups_size; i++)
@@ -1633,22 +1775,14 @@ void GenericCAO::processMessage(const std::string &data)
int rating = readS16(is);
m_armor_groups[name] = rating;
}
- } else if (cmd == GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) {
- // Deprecated, for backwards compatibility only.
- readU8(is); // version
- m_prop.nametag_color = readARGB8(is);
- if (m_nametag != NULL) {
- m_nametag->nametag_color = m_prop.nametag_color;
- v3f pos;
- pos.Y = m_prop.collisionbox.MaxEdge.Y + 0.3f;
- m_nametag->nametag_pos = pos;
- }
- } else if (cmd == GENERIC_CMD_SPAWN_INFANT) {
+ } else if (cmd == AO_CMD_SPAWN_INFANT) {
u16 child_id = readU16(is);
u8 type = readU8(is); // maybe this will be useful later
(void)type;
addAttachmentChild(child_id);
+ } else if (cmd == AO_CMD_OBSOLETE1) {
+ // Don't do anything and also don't log a warning
} else {
warningstream << FUNCTION_NAME
<< ": unknown command or outdated client \""
@@ -1684,13 +1818,11 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS);
m_env->addSimpleObject(simple);
}
- // TODO: Execute defined fast response
- // Flashing shall suffice as there is no definition
- if (m_reset_textures_timer < 0) {
+ if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) {
m_reset_textures_timer = 0.05;
if (result.damage >= 2)
m_reset_textures_timer += 0.05 * result.damage;
- updateTextures(m_current_texture_modifier + "^[brighten");
+ updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier);
}
}
diff --git a/src/client/content_cao.h b/src/client/content_cao.h
index 7c29cbf17..974ff9a1e 100644
--- a/src/client/content_cao.h
+++ b/src/client/content_cao.h
@@ -130,6 +130,8 @@ private:
// Settings
bool m_enable_shaders = false;
+ bool visualExpiryRequired(const ObjectProperties &newprops) const;
+
public:
GenericCAO(Client *client, ClientEnvironment *env);
@@ -186,10 +188,11 @@ public:
return m_matrixnode->getRelativeTransformationMatrix();
}
- inline const core::matrix4 &getAbsolutePosRotMatrix() const
+ inline const core::matrix4 *getAbsolutePosRotMatrix() const
{
- assert(m_matrixnode);
- return m_matrixnode->getAbsoluteTransformation();
+ if (!m_matrixnode)
+ return nullptr;
+ return &m_matrixnode->getAbsoluteTransformation();
}
inline f32 getStepHeight() const
@@ -234,13 +237,16 @@ public:
m_visuals_expired = true;
}
- void updateLight(u8 light_at_pos);
-
- void updateLightNoCheck(u8 light_at_pos);
+ void updateLight(u32 day_night_ratio);
void setNodeLight(u8 light);
- v3s16 getLightPosition();
+ /* Get light position(s).
+ * returns number of positions written into pos[], which must have space
+ * for at least 3 vectors. */
+ u16 getLightPosition(v3s16 *pos);
+
+ void updateNametag();
void updateNodePos();
diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp
index 9b4fd221e..3d06584c4 100644
--- a/src/client/content_mapblock.cpp
+++ b/src/client/content_mapblock.cpp
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <cmath>
#include "content_mapblock.h"
#include "util/numeric.h"
#include "util/directiontables.h"
@@ -366,6 +367,7 @@ void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *
void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
TileSpec *tiles, int tile_count)
{
+ bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
f32 texture_coord_buf[24];
f32 dx1 = box.MinEdge.X;
f32 dy1 = box.MinEdge.Y;
@@ -373,6 +375,14 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
f32 dx2 = box.MaxEdge.X;
f32 dy2 = box.MaxEdge.Y;
f32 dz2 = box.MaxEdge.Z;
+ if (scale) {
+ if (!txc) { // generate texture coords before scaling
+ generateCuboidTextureCoords(box, texture_coord_buf);
+ txc = texture_coord_buf;
+ }
+ box.MinEdge *= f->visual_scale;
+ box.MaxEdge *= f->visual_scale;
+ }
box.MinEdge += origin;
box.MaxEdge += origin;
if (!txc) {
@@ -405,8 +415,8 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing()
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
- c_flowing = nodedef->getId(f->liquid_alternative_flowing);
- c_source = nodedef->getId(f->liquid_alternative_source);
+ c_flowing = f->liquid_alternative_flowing_id;
+ c_source = f->liquid_alternative_source_id;
top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
if (draw_liquid_bottom) {
@@ -512,8 +522,7 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
return 0;
}
-void MapblockMeshGenerator::drawLiquidSides()
-{
+namespace {
struct LiquidFaceDesc {
v3s16 dir; // XZ
v3s16 p[2]; // XZ only; 1 means +, 0 means -
@@ -521,20 +530,23 @@ void MapblockMeshGenerator::drawLiquidSides()
struct UV {
int u, v;
};
- static const LiquidFaceDesc base_faces[4] = {
+ static const LiquidFaceDesc liquid_base_faces[4] = {
{v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
{v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
{v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
{v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
};
- static const UV base_vertices[4] = {
+ static const UV liquid_base_vertices[4] = {
{0, 1},
{1, 1},
{1, 0},
{0, 0}
};
+}
- for (const auto &face : base_faces) {
+void MapblockMeshGenerator::drawLiquidSides()
+{
+ for (const auto &face : liquid_base_faces) {
const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
// No face between nodes of the same liquid, unless there is node
@@ -554,7 +566,7 @@ void MapblockMeshGenerator::drawLiquidSides()
video::S3DVertex vertices[4];
for (int j = 0; j < 4; j++) {
- const UV &vertex = base_vertices[j];
+ const UV &vertex = liquid_base_vertices[j];
const v3s16 &base = face.p[vertex.u];
float v = vertex.v;
@@ -1193,15 +1205,14 @@ bool MapblockMeshGenerator::isSameRail(v3s16 dir)
(def2.getGroup(raillike_groupname) == raillike_group));
}
-void MapblockMeshGenerator::drawRaillikeNode()
-{
- static const v3s16 direction[4] = {
+namespace {
+ static const v3s16 rail_direction[4] = {
v3s16( 0, 0, 1),
v3s16( 0, 0, -1),
v3s16(-1, 0, 0),
v3s16( 1, 0, 0),
};
- static const int slope_angle[4] = {0, 180, 90, -90};
+ static const int rail_slope_angle[4] = {0, 180, 90, -90};
enum RailTile {
straight,
@@ -1214,8 +1225,8 @@ void MapblockMeshGenerator::drawRaillikeNode()
int angle;
};
static const RailDesc rail_kinds[16] = {
- // +x -x -z +z
- //-------------
+ // +x -x -z +z
+ //-------------
{straight, 0}, // . . . .
{straight, 0}, // . . . +Z
{straight, 0}, // . . -Z .
@@ -1233,7 +1244,10 @@ void MapblockMeshGenerator::drawRaillikeNode()
{junction, 270}, // +X -X -Z .
{ cross, 0}, // +X -X -Z +Z
};
+}
+void MapblockMeshGenerator::drawRaillikeNode()
+{
raillike_group = nodedef->get(n).getGroup(raillike_groupname);
int code = 0;
@@ -1241,14 +1255,14 @@ void MapblockMeshGenerator::drawRaillikeNode()
int tile_index;
bool sloped = false;
for (int dir = 0; dir < 4; dir++) {
- bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
+ bool rail_above = isSameRail(rail_direction[dir] + v3s16(0, 1, 0));
if (rail_above) {
sloped = true;
- angle = slope_angle[dir];
+ angle = rail_slope_angle[dir];
}
if (rail_above ||
- isSameRail(direction[dir]) ||
- isSameRail(direction[dir] + v3s16(0, -1, 0)))
+ isSameRail(rail_direction[dir]) ||
+ isSameRail(rail_direction[dir] + v3s16(0, -1, 0)))
code |= 1 << dir;
}
@@ -1276,9 +1290,8 @@ void MapblockMeshGenerator::drawRaillikeNode()
drawQuad(vertices);
}
-void MapblockMeshGenerator::drawNodeboxNode()
-{
- static const v3s16 tile_dirs[6] = {
+namespace {
+ static const v3s16 nodebox_tile_dirs[6] = {
v3s16(0, 1, 0),
v3s16(0, -1, 0),
v3s16(1, 0, 0),
@@ -1288,7 +1301,7 @@ void MapblockMeshGenerator::drawNodeboxNode()
};
// we have this order for some reason...
- static const v3s16 connection_dirs[6] = {
+ static const v3s16 nodebox_connection_dirs[6] = {
v3s16( 0, 1, 0), // top
v3s16( 0, -1, 0), // bottom
v3s16( 0, 0, -1), // front
@@ -1296,19 +1309,22 @@ void MapblockMeshGenerator::drawNodeboxNode()
v3s16( 0, 0, 1), // back
v3s16( 1, 0, 0), // right
};
+}
+void MapblockMeshGenerator::drawNodeboxNode()
+{
TileSpec tiles[6];
for (int face = 0; face < 6; face++) {
// Handles facedir rotation for textures
- getTile(tile_dirs[face], &tiles[face]);
+ getTile(nodebox_tile_dirs[face], &tiles[face]);
}
// locate possible neighboring nodes to connect to
- int neighbors_set = 0;
+ u8 neighbors_set = 0;
if (f->node_box.type == NODEBOX_CONNECTED) {
for (int dir = 0; dir != 6; dir++) {
- int flag = 1 << dir;
- v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
+ u8 flag = 1 << dir;
+ v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
if (nodedef->nodeboxConnects(n, n2, flag))
neighbors_set |= flag;
@@ -1317,7 +1333,7 @@ void MapblockMeshGenerator::drawNodeboxNode()
std::vector<aabb3f> boxes;
n.getNodeBoxes(nodedef, &boxes, neighbors_set);
- for (const auto &box : boxes)
+ for (auto &box : boxes)
drawAutoLightedCuboid(box, nullptr, tiles, 6);
}
diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp
index 3d1b302a8..46bbe4059 100644
--- a/src/client/filecache.cpp
+++ b/src/client/filecache.cpp
@@ -82,8 +82,16 @@ bool FileCache::update(const std::string &name, const std::string &data)
std::string path = m_dir + DIR_DELIM + name;
return updateByPath(path, data);
}
+
bool FileCache::load(const std::string &name, std::ostream &os)
{
std::string path = m_dir + DIR_DELIM + name;
return loadByPath(path, os);
}
+
+bool FileCache::exists(const std::string &name)
+{
+ std::string path = m_dir + DIR_DELIM + name;
+ std::ifstream fis(path.c_str(), std::ios_base::binary);
+ return fis.good();
+}
diff --git a/src/client/filecache.h b/src/client/filecache.h
index 96e4c8ba1..ea6afc4b2 100644
--- a/src/client/filecache.h
+++ b/src/client/filecache.h
@@ -33,6 +33,7 @@ public:
bool update(const std::string &name, const std::string &data);
bool load(const std::string &name, std::ostream &os);
+ bool exists(const std::string &name);
private:
std::string m_dir;
diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp
index 2b5841cd8..61d52cc2f 100644
--- a/src/client/fontengine.cpp
+++ b/src/client/fontengine.cpp
@@ -239,7 +239,7 @@ void FontEngine::updateSkin()
FATAL_ERROR_IF(font == NULL, "Could not create/get font");
u32 text_height = font->getDimension(L"Hello, world!").Height;
- infostream << "text_height=" << text_height << std::endl;
+ infostream << "FontEngine: measured text_height=" << text_height << std::endl;
}
/******************************************************************************/
diff --git a/src/client/game.cpp b/src/client/game.cpp
index 0201ded69..069c482ca 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -266,6 +266,7 @@ class SoundMaker
public:
bool makes_footstep_sound;
float m_player_step_timer;
+ float m_player_jump_timer;
SimpleSoundSpec m_player_step_sound;
SimpleSoundSpec m_player_leftpunch_sound;
@@ -275,7 +276,8 @@ public:
m_sound(sound),
m_ndef(ndef),
makes_footstep_sound(true),
- m_player_step_timer(0)
+ m_player_step_timer(0.0f),
+ m_player_jump_timer(0.0f)
{
}
@@ -288,6 +290,14 @@ public:
}
}
+ void playPlayerJump()
+ {
+ if (m_player_jump_timer <= 0.0f) {
+ m_player_jump_timer = 0.2f;
+ m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
+ }
+ }
+
static void viewBobbingStep(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
@@ -302,7 +312,8 @@ public:
static void playerJump(MtEvent *e, void *data)
{
- //SoundMaker *sm = (SoundMaker*)data;
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->playPlayerJump();
}
static void cameraPunchLeft(MtEvent *e, void *data)
@@ -351,6 +362,7 @@ public:
void step(float dtime)
{
m_player_step_timer -= dtime;
+ m_player_jump_timer -= dtime;
}
};
@@ -843,6 +855,7 @@ private:
SoundMaker *soundmaker = nullptr;
ChatBackend *chat_backend = nullptr;
+ LogOutputBuffer m_chat_log_buf;
EventManager *eventmgr = nullptr;
QuicktuneShortcutter *quicktune = nullptr;
@@ -914,6 +927,7 @@ private:
};
Game::Game() :
+ m_chat_log_buf(g_logger),
m_game_ui(new GameUI())
{
g_settings->registerChangedCallback("doubletap_jump",
@@ -1043,7 +1057,7 @@ bool Game::startup(bool *kill,
m_invert_mouse = g_settings->getBool("invert_mouse");
m_first_loop_after_window_activation = true;
- g_translations->clear();
+ g_client_translations->clear();
if (!init(map_dir, address, port, gamespec))
return false;
@@ -1159,6 +1173,10 @@ void Game::shutdown()
if (formspec)
formspec->quitMenu();
+#ifdef HAVE_TOUCHSCREENGUI
+ g_touchscreengui->hide();
+#endif
+
showOverlayMessage(N_("Shutting down..."), 0, 0, false);
if (clouds)
@@ -1180,6 +1198,7 @@ void Game::shutdown()
chat_backend->addMessage(L"", L"# Disconnected.");
chat_backend->addMessage(L"", L"");
+ m_chat_log_buf.clear();
if (client) {
client->Stop();
@@ -1237,7 +1256,7 @@ bool Game::init(
bool Game::initSound()
{
#if USE_SOUND
- if (g_settings->getBool("enable_sound")) {
+ if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) {
infostream << "Attempting to use OpenAL audio" << std::endl;
sound = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher);
if (!sound)
@@ -1290,7 +1309,6 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
}
server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false);
- server->init();
server->start();
return true;
@@ -1407,8 +1425,11 @@ bool Game::createClient(const std::string &playername,
}
mapper = client->getMinimap();
- if (mapper)
+ if (mapper) {
mapper->setMinimapMode(MINIMAP_MODE_OFF);
+ if (client->modsLoaded())
+ client->getScript()->on_minimap_ready(mapper);
+ }
return true;
}
@@ -1553,7 +1574,8 @@ bool Game::connectToServer(const std::string &playername,
} else {
registration_confirmation_shown = true;
(new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
- &g_menumgr, client, playername, password, connection_aborted))->drop();
+ &g_menumgr, client, playername, password,
+ connection_aborted, texture_src))->drop();
}
} else {
wait_time += dtime;
@@ -1708,19 +1730,19 @@ inline bool Game::handleCallbacks()
if (g_gamecallback->changepassword_requested) {
(new GUIPasswordChange(guienv, guiroot, -1,
- &g_menumgr, client))->drop();
+ &g_menumgr, client, texture_src))->drop();
g_gamecallback->changepassword_requested = false;
}
if (g_gamecallback->changevolume_requested) {
(new GUIVolumeChange(guienv, guiroot, -1,
- &g_menumgr))->drop();
+ &g_menumgr, texture_src))->drop();
g_gamecallback->changevolume_requested = false;
}
if (g_gamecallback->keyconfig_requested) {
(new GUIKeyChangeMenu(guienv, guiroot, -1,
- &g_menumgr))->drop();
+ &g_menumgr, texture_src))->drop();
g_gamecallback->keyconfig_requested = false;
}
@@ -1906,29 +1928,47 @@ void Game::processKeyInput()
toggleFast();
} else if (wasKeyDown(KeyType::NOCLIP)) {
toggleNoClip();
+#if USE_SOUND
} else if (wasKeyDown(KeyType::MUTE)) {
- bool new_mute_sound = !g_settings->getBool("mute_sound");
- g_settings->setBool("mute_sound", new_mute_sound);
- if (new_mute_sound)
- m_game_ui->showTranslatedStatusText("Sound muted");
- else
- m_game_ui->showTranslatedStatusText("Sound unmuted");
+ if (g_settings->getBool("enable_sound")) {
+ bool new_mute_sound = !g_settings->getBool("mute_sound");
+ g_settings->setBool("mute_sound", new_mute_sound);
+ if (new_mute_sound)
+ m_game_ui->showTranslatedStatusText("Sound muted");
+ else
+ m_game_ui->showTranslatedStatusText("Sound unmuted");
+ } else {
+ m_game_ui->showTranslatedStatusText("Sound system is disabled");
+ }
} else if (wasKeyDown(KeyType::INC_VOLUME)) {
- float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
- wchar_t buf[100];
- g_settings->setFloat("sound_volume", new_volume);
- const wchar_t *str = wgettext("Volume changed to %d%%");
- swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
- delete[] str;
- m_game_ui->showStatusText(buf);
+ if (g_settings->getBool("enable_sound")) {
+ float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
+ wchar_t buf[100];
+ g_settings->setFloat("sound_volume", new_volume);
+ const wchar_t *str = wgettext("Volume changed to %d%%");
+ swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
+ delete[] str;
+ m_game_ui->showStatusText(buf);
+ } else {
+ m_game_ui->showTranslatedStatusText("Sound system is disabled");
+ }
} else if (wasKeyDown(KeyType::DEC_VOLUME)) {
- float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
- wchar_t buf[100];
- g_settings->setFloat("sound_volume", new_volume);
- const wchar_t *str = wgettext("Volume changed to %d%%");
- swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
- delete[] str;
- m_game_ui->showStatusText(buf);
+ if (g_settings->getBool("enable_sound")) {
+ float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
+ wchar_t buf[100];
+ g_settings->setFloat("sound_volume", new_volume);
+ const wchar_t *str = wgettext("Volume changed to %d%%");
+ swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
+ delete[] str;
+ m_game_ui->showStatusText(buf);
+ } else {
+ m_game_ui->showTranslatedStatusText("Sound system is disabled");
+ }
+#else
+ } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME)
+ || wasKeyDown(KeyType::DEC_VOLUME)) {
+ m_game_ui->showTranslatedStatusText("Sound system is not supported on this build");
+#endif
} else if (wasKeyDown(KeyType::CINEMATIC)) {
toggleCinematic();
} else if (wasKeyDown(KeyType::SCREENSHOT)) {
@@ -2010,7 +2050,6 @@ void Game::processItemSelection(u16 *new_playeritem)
for (u16 i = 0; i <= max_item; i++) {
if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) {
*new_playeritem = i;
- infostream << "Selected item: " << new_playeritem << std::endl;
break;
}
}
@@ -2039,7 +2078,7 @@ void Game::openInventory()
if (!player || !player->getCAO())
return;
- infostream << "the_game: " << "Launching inventory" << std::endl;
+ infostream << "Game: Launching inventory" << std::endl;
PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
@@ -2451,7 +2490,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE)
);
- u32 keypress_bits =
+ u32 keypress_bits = (
( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) |
( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) |
( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) |
@@ -2460,7 +2499,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) |
( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) |
( (u32)(input->getLeftState() & 0x1) << 7) |
- ( (u32)(input->getRightState() & 0x1) << 8
+ ( (u32)(input->getRightState() & 0x1) << 8) |
+ ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9)
);
#ifdef ANDROID
@@ -2640,6 +2680,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
delete event->hudadd.offset;
delete event->hudadd.world_pos;
delete event->hudadd.size;
+ delete event->hudadd.text2;
return;
}
@@ -2657,6 +2698,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
e->world_pos = *event->hudadd.world_pos;
e->size = *event->hudadd.size;
e->z_index = event->hudadd.z_index;
+ e->text2 = *event->hudadd.text2;
hud_server_to_client[server_id] = player->addHud(e);
delete event->hudadd.pos;
@@ -2667,6 +2709,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
delete event->hudadd.offset;
delete event->hudadd.world_pos;
delete event->hudadd.size;
+ delete event->hudadd.text2;
}
void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam)
@@ -2739,6 +2782,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca
case HUD_STAT_Z_INDEX:
e->z_index = event->hudchange.data;
break;
+
+ case HUD_STAT_TEXT2:
+ e->text2 = *event->hudchange.sdata;
+ break;
}
delete event->hudchange.v3fdata;
@@ -2764,11 +2811,11 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
// Shows the mesh skybox
sky->setVisible(true);
// Update mesh based skybox colours if applicable.
- sky->setSkyColors(*event->set_sky);
+ sky->setSkyColors(event->set_sky->sky_color);
sky->setHorizonTint(
- event->set_sky->sun_tint,
- event->set_sky->moon_tint,
- event->set_sky->tint_type
+ event->set_sky->fog_sun_tint,
+ event->set_sky->fog_moon_tint,
+ event->set_sky->fog_tint_type
);
} else if (event->set_sky->type == "skybox" &&
event->set_sky->textures.size() == 6) {
@@ -2778,9 +2825,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
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
+ event->set_sky->fog_sun_tint,
+ event->set_sky->fog_moon_tint,
+ event->set_sky->fog_tint_type
);
// Add textures to skybox.
for (int i = 0; i < 6; i++)
@@ -2864,18 +2911,9 @@ void Game::processClientEvents(CameraOrientation *cam)
void Game::updateChat(f32 dtime, const v2u32 &screensize)
{
- // Add chat log output for errors to be shown in chat
- static LogOutputBuffer chat_log_error_buf(g_logger, LL_ERROR);
-
// Get new messages from error log buffer
- while (!chat_log_error_buf.empty()) {
- std::wstring error_message = utf8_to_wide(chat_log_error_buf.get());
- if (!g_settings->getBool("disable_escape_sequences")) {
- error_message.insert(0, L"\x1b(c@red)");
- error_message.append(L"\x1b(c@white)");
- }
- chat_backend->addMessage(L"", error_message);
- }
+ while (!m_chat_log_buf.empty())
+ chat_backend->addMessage(L"", utf8_to_wide(m_chat_log_buf.get()));
// Get new messages from client
std::wstring message;
@@ -2996,16 +3034,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
- v3f player_position = player->getPosition();
- v3f player_eye_position = player->getEyePosition();
- v3f camera_position = camera->getPosition();
- v3f camera_direction = camera->getDirection();
- v3s16 camera_offset = camera->getOffset();
-
- if (camera->getCameraMode() == CAMERA_MODE_FIRST)
- player_eye_position += player->eye_offset_first;
- else
- player_eye_position += player->eye_offset_third;
+ const v3f camera_direction = camera->getDirection();
+ const v3s16 camera_offset = camera->getOffset();
/*
Calculate what block is the crosshair pointing to
@@ -3019,13 +3049,22 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
core::line3d<f32> shootline;
- if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) {
- shootline = core::line3d<f32>(player_eye_position,
- player_eye_position + camera_direction * BS * d);
- } else {
+ switch (camera->getCameraMode()) {
+ case CAMERA_MODE_FIRST:
+ // Shoot from camera position, with bobbing
+ shootline.start = camera->getPosition();
+ break;
+ case CAMERA_MODE_THIRD:
+ // Shoot from player head, no bobbing
+ shootline.start = camera->getHeadPosition();
+ break;
+ case CAMERA_MODE_THIRD_FRONT:
+ shootline.start = camera->getHeadPosition();
// prevent player pointing anything in front-view
- shootline = core::line3d<f32>(camera_position, camera_position);
+ d = 0;
+ break;
}
+ shootline.end = shootline.start + camera_direction * BS * d;
#ifdef HAVE_TOUCHSCREENGUI
@@ -3112,6 +3151,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
} else if (pointed.type == POINTEDTHING_NODE) {
handlePointingAtNode(pointed, selected_item, hand_item, dtime);
} else if (pointed.type == POINTEDTHING_OBJECT) {
+ v3f player_position = player->getPosition();
handlePointingAtObject(pointed, tool_item, player_position, show_debug);
} else if (input->getLeftState()) {
// When button is held down in air, show continuous animation
@@ -4147,8 +4187,12 @@ void Game::showPauseMenu()
}
#ifndef __ANDROID__
- os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
- << strgettext("Sound Volume") << "]";
+#if USE_SOUND
+ if (g_settings->getBool("enable_sound")) {
+ os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
+ << strgettext("Sound Volume") << "]";
+ }
+#endif
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
<< strgettext("Change Keys") << "]";
#endif
@@ -4245,7 +4289,6 @@ void the_game(bool *kill,
reconnect_requested, &chat_backend, gamespec,
simple_singleplayer_mode)) {
game.run();
- game.shutdown();
}
} catch (SerializationError &e) {
@@ -4261,4 +4304,5 @@ void the_game(bool *kill,
strgettext("\nCheck debug.txt for details.");
errorstream << error_message << std::endl;
}
+ game.shutdown();
}
diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp
index 138dfb4da..c216f405d 100644
--- a/src/client/gameui.cpp
+++ b/src/client/gameui.cpp
@@ -76,6 +76,11 @@ void GameUI::init()
m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
false, true, guiroot);
+ u16 chat_font_size = g_settings->getU16("chat_font_size");
+ if (chat_font_size != 0) {
+ m_guitext_chat->setOverrideFont(g_fontengine->getFont(
+ chat_font_size, FM_Unspecified));
+ }
// Profiler text (size is updated when text is updated)
m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
@@ -128,9 +133,9 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
<< "pos: (" << (player_position.X / BS)
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
- << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
+ << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "\xC2\xB0 "
<< yawToDirectionString(cam.camera_yaw)
- << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°"
+ << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "\xC2\xB0"
<< " | seed: " << ((u64)client->getMapSeed());
if (pointed_old.type == POINTEDTHING_NODE) {
@@ -213,7 +218,6 @@ void GameUI::showTranslatedStatusText(const char *str)
void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
{
- setStaticText(m_guitext_chat, chat_text);
// Update gui element size and position
s32 chat_y = 5;
@@ -221,16 +225,15 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
if (m_flags.show_debug)
chat_y += 2 * g_fontengine->getLineHeight();
- // first pass to calculate height of text to be set
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
- s32 width = std::min(g_fontengine->getTextWidth(chat_text.c_str()) + 10,
- window_size.X - 20);
- m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
- chat_y + window_size.Y));
-
- // now use real height of text and adjust rect according to this size
- m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
- chat_y + m_guitext_chat->getTextHeight()));
+
+ core::rect<s32> chat_size(10, chat_y,
+ window_size.X - 20, 0);
+ chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y,
+ m_guitext_chat->getTextHeight() + chat_y);
+
+ m_guitext_chat->setRelativePosition(chat_size);
+ setStaticText(m_guitext_chat, chat_text);
m_recent_chat_count = recent_chat_count;
}
diff --git a/src/client/hud.cpp b/src/client/hud.cpp
index 37de6640b..31e633bc2 100644
--- a/src/client/hud.cpp
+++ b/src/client/hud.cpp
@@ -51,6 +51,7 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
this->inventory = inventory;
m_hud_scaling = g_settings->getFloat("hud_scaling");
+ m_scale_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5f);
m_hotbar_imagesize *= m_hud_scaling;
@@ -213,9 +214,7 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
}
// Position of upper left corner of bar
- v2s32 pos = screen_offset;
- pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
- pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
+ v2s32 pos = screen_offset * m_scale_factor;
pos += upperleftpos;
// Store hotbar_image in member variable, used by drawItem()
@@ -272,6 +271,25 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
}
}
+// Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
+bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
+{
+ v3f w_pos = e->world_pos * BS;
+ scene::ICameraSceneNode* camera =
+ RenderingEngine::get_scene_manager()->getActiveCamera();
+ w_pos -= intToFloat(camera_offset, BS);
+ core::matrix4 trans = camera->getProjectionMatrix();
+ trans *= camera->getViewMatrix();
+ f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
+ trans.multiplyWith1x4Matrix(transformed_pos);
+ if (transformed_pos[3] < 0)
+ return false;
+ f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
+ core::reciprocal(transformed_pos[3]);
+ pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
+ pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
+ return true;
+}
void Hud::drawLuaElements(const v3s16 &camera_offset)
{
@@ -299,43 +317,47 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
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) {
- case HUD_ELEM_IMAGE: {
- video::ITexture *texture = tsrc->getTexture(e->text);
- if (!texture)
- continue;
-
- const video::SColor color(255, 255, 255, 255);
- const video::SColor colors[] = {color, color, color, color};
- core::dimension2di imgsize(texture->getOriginalSize());
- v2s32 dstsize(imgsize.Width * e->scale.X,
- imgsize.Height * e->scale.Y);
- if (e->scale.X < 0)
- dstsize.X = m_screensize.X * (e->scale.X * -0.01);
- if (e->scale.Y < 0)
- dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
- v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
- (e->align.Y - 1.0) * dstsize.Y / 2);
- core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
- rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
- draw2DImageFilterScaled(driver, texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0), imgsize),
- NULL, colors, true);
- break; }
case HUD_ELEM_TEXT: {
+ irr::gui::IGUIFont *textfont = font;
+ unsigned int font_size = g_fontengine->getDefaultFontSize();
+
+ if (e->size.X > 0)
+ font_size *= e->size.X;
+
+ if (font_size != g_fontengine->getDefaultFontSize())
+ textfont = g_fontengine->getFont(font_size);
+
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
- core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = unescape_translate(utf8_to_wide(e->text));
- core::dimension2d<u32> textsize = font->getDimension(text.c_str());
+ core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
+#ifdef __ANDROID__
+ // The text size on Android is not proportional with the actual scaling
+ irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
+ textfont : g_fontengine->getFont(font_size - 3);
+ if (e->offset.X < -20)
+ textsize = font_scaled->getDimension(text.c_str());
+#endif
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
- v2s32 offs(e->offset.X, e->offset.Y);
- font->draw(text.c_str(), size + pos + offset + offs, color);
+ core::rect<s32> size(0, 0, e->scale.X * m_scale_factor,
+ text_height * e->scale.Y * m_scale_factor);
+ v2s32 offs(e->offset.X * m_scale_factor,
+ e->offset.Y * m_scale_factor);
+#ifdef __ANDROID__
+ if (e->offset.X < -20)
+ font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
+ else
+#endif
+ {
+ textfont->draw(text.c_str(), size + pos + offset + offs, color);
+ }
break; }
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
- drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
+ drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
+ e->number, e->item, offs, e->size);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
@@ -343,34 +365,59 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
- v3f p_pos = player->getPosition() / BS;
- v3f w_pos = e->world_pos * BS;
- float distance = std::floor(10 * p_pos.getDistanceFrom(e->world_pos)) /
- 10.0f;
- scene::ICameraSceneNode* camera =
- RenderingEngine::get_scene_manager()->getActiveCamera();
- w_pos -= intToFloat(camera_offset, BS);
- core::matrix4 trans = camera->getProjectionMatrix();
- trans *= camera->getViewMatrix();
- f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
- trans.multiplyWith1x4Matrix(transformed_pos);
- if (transformed_pos[3] < 0)
+ if (!calculateScreenPos(camera_offset, e, &pos))
break;
- f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
- core::reciprocal(transformed_pos[3]);
- pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
- pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
+ v3f p_pos = player->getPosition() / BS;
+ pos += v2s32(e->offset.X, e->offset.Y);
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
- core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
- font->draw(text.c_str(), size + pos, color);
- std::ostringstream os;
- os << distance << e->text;
- text = unescape_translate(utf8_to_wide(os.str()));
- pos.Y += text_height;
- font->draw(text.c_str(), size + pos, color);
+ const std::string &unit = e->text;
+ // waypoints reuse the item field to store precision, item = precision + 1
+ u32 item = e->item;
+ float precision = (item == 0) ? 10.0f : (item - 1.f);
+ bool draw_precision = precision > 0;
+
+ core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
+ pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
+ bounds += pos;
+ font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
+ if (draw_precision) {
+ std::ostringstream os;
+ float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
+ os << distance << unit;
+ text = unescape_translate(utf8_to_wide(os.str()));
+ bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
+ font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
+ }
+ break; }
+ case HUD_ELEM_IMAGE_WAYPOINT: {
+ if (!calculateScreenPos(camera_offset, e, &pos))
+ break;
+ }
+ case HUD_ELEM_IMAGE: {
+ video::ITexture *texture = tsrc->getTexture(e->text);
+ if (!texture)
+ continue;
+
+ const video::SColor color(255, 255, 255, 255);
+ const video::SColor colors[] = {color, color, color, color};
+ core::dimension2di imgsize(texture->getOriginalSize());
+ v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
+ imgsize.Height * e->scale.Y * m_scale_factor);
+ if (e->scale.X < 0)
+ dstsize.X = m_screensize.X * (e->scale.X * -0.01);
+ if (e->scale.Y < 0)
+ dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
+ v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
+ (e->align.Y - 1.0) * dstsize.Y / 2);
+ core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
+ rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
+ e->offset.Y * m_scale_factor);
+ draw2DImageFilterScaled(driver, texture, rect,
+ core::rect<s32>(core::position2d<s32>(0,0), imgsize),
+ NULL, colors, true);
break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
@@ -380,8 +427,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
}
-void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture,
- s32 count, v2s32 offset, v2s32 size)
+void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
+ const std::string &texture, const std::string &bgtexture,
+ s32 count, s32 maxcount, v2s32 offset, v2s32 size)
{
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
@@ -390,16 +438,24 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex
if (!stat_texture)
return;
+ video::ITexture *stat_texture_bg = nullptr;
+ if (!bgtexture.empty()) {
+ stat_texture_bg = tsrc->getTexture(bgtexture);
+ }
+
core::dimension2di srcd(stat_texture->getOriginalSize());
core::dimension2di dstd;
if (size == v2s32()) {
dstd = srcd;
+ dstd.Height *= m_scale_factor;
+ dstd.Width *= m_scale_factor;
+ offset.X *= m_scale_factor;
+ offset.Y *= m_scale_factor;
} else {
- float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
- dstd.Height = size.Y * size_factor;
- dstd.Width = size.X * size_factor;
- offset.X *= size_factor;
- offset.Y *= size_factor;
+ dstd.Height = size.Y * m_scale_factor;
+ dstd.Width = size.X * m_scale_factor;
+ offset.X *= m_scale_factor;
+ offset.Y *= m_scale_factor;
}
v2s32 p = pos;
@@ -409,43 +465,100 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex
p += offset;
v2s32 steppos;
- core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
- srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
- dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
- srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
- dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
- srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
- dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
+ // From left to right
steppos = v2s32(1, 0);
- srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
- dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
+ break;
}
+
+ auto calculate_clipping_rect = [] (core::dimension2di src,
+ v2s32 steppos) -> core::rect<s32> {
+
+ // Create basic rectangle
+ core::rect<s32> rect(0, 0,
+ src.Width - std::abs(steppos.X) * src.Width / 2,
+ src.Height - std::abs(steppos.Y) * src.Height / 2
+ );
+ // Move rectangle left or down
+ if (steppos.X == -1)
+ rect += v2s32(src.Width / 2, 0);
+ if (steppos.Y == -1)
+ rect += v2s32(0, src.Height / 2);
+ return rect;
+ };
+ // Rectangles for 1/2 the actual value to display
+ core::rect<s32> srchalfrect, dsthalfrect;
+ // Rectangles for 1/2 the "off state" texture
+ core::rect<s32> srchalfrect2, dsthalfrect2;
+
+ if (count % 2 == 1) {
+ // Need to draw halves: Calculate rectangles
+ srchalfrect = calculate_clipping_rect(srcd, steppos);
+ dsthalfrect = calculate_clipping_rect(dstd, steppos);
+ srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
+ dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
+ }
+
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
+ // Draw full textures
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
- core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
+ core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
dstrect += p;
- draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
+ draw2DImageFilterScaled(driver, stat_texture,
+ dstrect, srcrect, NULL, colors, true);
p += steppos;
}
if (count % 2 == 1) {
- dsthalfrect += p;
- draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
+ // Draw half a texture
+ draw2DImageFilterScaled(driver, stat_texture,
+ dsthalfrect + p, srchalfrect, NULL, colors, true);
+
+ if (stat_texture_bg && maxcount > count) {
+ draw2DImageFilterScaled(driver, stat_texture_bg,
+ dsthalfrect2 + p, srchalfrect2,
+ NULL, colors, true);
+ p += steppos;
+ }
+ }
+
+ if (stat_texture_bg && maxcount > count / 2) {
+ // Draw "off state" textures
+ s32 start_offset;
+ if (count % 2 == 1)
+ start_offset = count / 2 + 1;
+ else
+ start_offset = count / 2;
+ for (s32 i = start_offset; i < maxcount / 2; i++) {
+ core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
+ core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
+
+ dstrect += p;
+ draw2DImageFilterScaled(driver, stat_texture_bg,
+ dstrect, srcrect,
+ NULL, colors, true);
+ p += steppos;
+ }
+
+ if (maxcount % 2 == 1) {
+ draw2DImageFilterScaled(driver, stat_texture_bg,
+ dsthalfrect + p, srchalfrect,
+ NULL, colors, true);
+ }
}
}
@@ -465,7 +578,7 @@ void Hud::drawHotbar(u16 playeritem) {
v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
- if ( (float) width / (float) window_size.X <=
+ if ((float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
@@ -517,12 +630,10 @@ void Hud::drawSelectionMesh()
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
- for (std::vector<aabb3f>::const_iterator
- i = m_selection_boxes.begin();
- i != m_selection_boxes.end(); ++i) {
+ for (auto & selection_box : m_selection_boxes) {
aabb3f box = aabb3f(
- i->MinEdge + m_selection_pos_with_offset,
- i->MaxEdge + m_selection_pos_with_offset);
+ selection_box.MinEdge + m_selection_pos_with_offset,
+ selection_box.MaxEdge + m_selection_pos_with_offset);
u32 r = (selectionbox_argb.getRed() *
m_selection_mesh_color.getRed() / 255);
diff --git a/src/client/hud.h b/src/client/hud.h
index d9b5e0686..6f4c54626 100644
--- a/src/client/hud.h
+++ b/src/client/hud.h
@@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CLIENT_HUD_HEADER
-#define CLIENT_HUD_HEADER
+#pragma once
#include <vector>
#include <IGUIFont.h>
@@ -81,8 +80,10 @@ public:
void drawLuaElements(const v3s16 &camera_offset);
private:
- void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture,
- s32 count, v2s32 offset, v2s32 size = v2s32());
+ bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos);
+ void drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
+ const std::string &texture, const std::string& bgtexture,
+ s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32());
void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem,
@@ -91,6 +92,7 @@ private:
void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected);
float m_hud_scaling; // cached minetest setting
+ float m_scale_factor;
v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
@@ -145,4 +147,3 @@ void drawItemStack(
const v3s16 &angle,
const v3s16 &rotation_speed);
-#endif
diff --git a/src/client/imagefilters.cpp b/src/client/imagefilters.cpp
index dd029628f..0fa501410 100644
--- a/src/client/imagefilters.cpp
+++ b/src/client/imagefilters.cpp
@@ -91,7 +91,7 @@ void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::I
u32 dy, dx;
video::SColor pxl;
- // Cache rectsngle boundaries.
+ // Cache rectangle boundaries.
double sox = srcrect.UpperLeftCorner.X * 1.0;
double soy = srcrect.UpperLeftCorner.Y * 1.0;
double sw = srcrect.getWidth() * 1.0;
@@ -107,15 +107,15 @@ void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::I
// Do some basic clipping, and for mirrored/flipped rects,
// make sure min/max are in the right order.
minsx = sox + (dx * sw / dim.Width);
- minsx = rangelim(minsx, 0, sw);
+ minsx = rangelim(minsx, 0, sox + sw);
maxsx = minsx + sw / dim.Width;
- maxsx = rangelim(maxsx, 0, sw);
+ maxsx = rangelim(maxsx, 0, sox + sw);
if (minsx > maxsx)
SWAP(double, minsx, maxsx);
minsy = soy + (dy * sh / dim.Height);
- minsy = rangelim(minsy, 0, sh);
+ minsy = rangelim(minsy, 0, soy + sh);
maxsy = minsy + sh / dim.Height;
- maxsy = rangelim(maxsy, 0, sh);
+ maxsy = rangelim(maxsy, 0, soy + sh);
if (minsy > maxsy)
SWAP(double, minsy, maxsy);
diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp
index c20c3619f..1e7040d57 100644
--- a/src/client/localplayer.cpp
+++ b/src/client/localplayer.cpp
@@ -200,6 +200,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
if (noclip && free_move) {
position += m_speed * dtime;
setPosition(position);
+
+ touching_ground = false;
added_velocity = v3f(0.0f); // ignored
return;
}
@@ -436,9 +438,11 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
+ const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
// Determine if jumping is possible
- m_disable_jump = itemgroup_get(f.groups, "disable_jump");
+ m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
+ itemgroup_get(f1.groups, "disable_jump");
m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
// Jump key pressed while jumping off from a bouncy block
@@ -785,6 +789,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
if (free_move) {
position += m_speed * dtime;
setPosition(position);
+
+ touching_ground = false;
m_sneak_node_exists = false;
added_velocity = v3f(0.0f);
return;
diff --git a/src/client/localplayer.h b/src/client/localplayer.h
index 95dceb1f4..345aec9d9 100644
--- a/src/client/localplayer.h
+++ b/src/client/localplayer.h
@@ -135,12 +135,17 @@ public:
}
v3f getPosition() const { return m_position; }
+
+ // Non-transformed eye offset getters
+ // For accurate positions, use the Camera functions
v3f getEyePosition() const { return m_position + getEyeOffset(); }
v3f getEyeOffset() const;
void setEyeHeight(float eye_height) { m_eye_height = eye_height; }
void setCollisionbox(const aabb3f &box) { m_collisionbox = box; }
+ const aabb3f& getCollisionbox() const { return m_collisionbox; }
+
float getZoomFOV() const { return m_zoom_fov; }
void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; }
diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp
index a5bee6b88..1020e35f5 100644
--- a/src/client/mapblock_mesh.cpp
+++ b/src/client/mapblock_mesh.cpp
@@ -225,7 +225,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
return f.light_propagates;
};
- std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
+ bool obstructed[4] = { true, true, true, true };
add_node(0);
bool opaque1 = !add_node(1);
bool opaque2 = !add_node(2);
@@ -372,6 +372,32 @@ void final_color_blend(video::SColor *result,
Mesh generation helpers
*/
+// This table is moved outside getNodeVertexDirs to avoid the compiler using
+// a mutex to initialize this table at runtime right in the hot path.
+// For details search the internet for "cxa_guard_acquire".
+static const v3s16 vertex_dirs_table[] = {
+ // ( 1, 0, 0)
+ v3s16( 1,-1, 1), v3s16( 1,-1,-1),
+ v3s16( 1, 1,-1), v3s16( 1, 1, 1),
+ // ( 0, 1, 0)
+ v3s16( 1, 1,-1), v3s16(-1, 1,-1),
+ v3s16(-1, 1, 1), v3s16( 1, 1, 1),
+ // ( 0, 0, 1)
+ v3s16(-1,-1, 1), v3s16( 1,-1, 1),
+ v3s16( 1, 1, 1), v3s16(-1, 1, 1),
+ // invalid
+ v3s16(), v3s16(), v3s16(), v3s16(),
+ // ( 0, 0,-1)
+ v3s16( 1,-1,-1), v3s16(-1,-1,-1),
+ v3s16(-1, 1,-1), v3s16( 1, 1,-1),
+ // ( 0,-1, 0)
+ v3s16( 1,-1, 1), v3s16(-1,-1, 1),
+ v3s16(-1,-1,-1), v3s16( 1,-1,-1),
+ // (-1, 0, 0)
+ v3s16(-1,-1,-1), v3s16(-1,-1, 1),
+ v3s16(-1, 1, 1), v3s16(-1, 1,-1)
+};
+
/*
vertex_dirs: v3s16[4]
*/
@@ -384,44 +410,16 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
2: top-left
3: top-right
*/
- if (dir == v3s16(0, 0, 1)) {
- // If looking towards z+, this is the face that is behind
- // the center point, facing towards z+.
- vertex_dirs[0] = v3s16(-1,-1, 1);
- vertex_dirs[1] = v3s16( 1,-1, 1);
- vertex_dirs[2] = v3s16( 1, 1, 1);
- vertex_dirs[3] = v3s16(-1, 1, 1);
- } else if (dir == v3s16(0, 0, -1)) {
- // faces towards Z-
- vertex_dirs[0] = v3s16( 1,-1,-1);
- vertex_dirs[1] = v3s16(-1,-1,-1);
- vertex_dirs[2] = v3s16(-1, 1,-1);
- vertex_dirs[3] = v3s16( 1, 1,-1);
- } else if (dir == v3s16(1, 0, 0)) {
- // faces towards X+
- vertex_dirs[0] = v3s16( 1,-1, 1);
- vertex_dirs[1] = v3s16( 1,-1,-1);
- vertex_dirs[2] = v3s16( 1, 1,-1);
- vertex_dirs[3] = v3s16( 1, 1, 1);
- } else if (dir == v3s16(-1, 0, 0)) {
- // faces towards X-
- vertex_dirs[0] = v3s16(-1,-1,-1);
- vertex_dirs[1] = v3s16(-1,-1, 1);
- vertex_dirs[2] = v3s16(-1, 1, 1);
- vertex_dirs[3] = v3s16(-1, 1,-1);
- } else if (dir == v3s16(0, 1, 0)) {
- // faces towards Y+ (assume Z- as "down" in texture)
- vertex_dirs[0] = v3s16( 1, 1,-1);
- vertex_dirs[1] = v3s16(-1, 1,-1);
- vertex_dirs[2] = v3s16(-1, 1, 1);
- vertex_dirs[3] = v3s16( 1, 1, 1);
- } else if (dir == v3s16(0, -1, 0)) {
- // faces towards Y- (assume Z+ as "down" in texture)
- vertex_dirs[0] = v3s16( 1,-1, 1);
- vertex_dirs[1] = v3s16(-1,-1, 1);
- vertex_dirs[2] = v3s16(-1,-1,-1);
- vertex_dirs[3] = v3s16( 1,-1,-1);
- }
+
+ // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
+ // (0,0,1), (0,0,-1)
+ assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
+
+ // Convert direction to single integer for table lookup
+ u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
+ idx = (idx - 1) * 4;
+
+ memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
}
static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
@@ -890,8 +888,10 @@ static void updateFastFaceRow(
v3s16 p_corrected;
v3s16 face_dir_corrected;
u16 lights[4] = {0, 0, 0, 0};
- u8 waving;
+ u8 waving = 0;
TileSpec tile;
+
+ // Get info of first tile
getTileInfo(data, p, face_dir,
makes_face, p_corrected, face_dir_corrected,
lights, waving, tile);
@@ -902,8 +902,6 @@ static void updateFastFaceRow(
// If tiling can be done, this is set to false in the next step
bool next_is_different = true;
- v3s16 p_next;
-
bool next_makes_face = false;
v3s16 next_p_corrected;
v3s16 next_face_dir_corrected;
@@ -912,9 +910,9 @@ static void updateFastFaceRow(
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
if (j != MAP_BLOCKSIZE - 1) {
- p_next = p + translate_dir;
+ p += translate_dir;
- getTileInfo(data, p_next, face_dir,
+ getTileInfo(data, p, face_dir,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
waving,
@@ -923,7 +921,7 @@ static void updateFastFaceRow(
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
+ && memcmp(next_lights, lights, sizeof(lights)) == 0
// Don't apply fast faces to waving water.
&& (waving != 3 || !waving_liquids)
&& next_tile.isTileable(tile)) {
@@ -961,10 +959,9 @@ static void updateFastFaceRow(
makes_face = next_makes_face;
p_corrected = next_p_corrected;
face_dir_corrected = next_face_dir_corrected;
- std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
+ memcpy(lights, next_lights, sizeof(lights));
if (next_is_different)
- tile = next_tile;
- p = p_next;
+ tile = std::move(next_tile); // faster than copy
}
}
diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp
index 4d73ead8a..e1ec22068 100644
--- a/src/client/mesh.cpp
+++ b/src/client/mesh.cpp
@@ -328,6 +328,26 @@ void recalculateBoundingBox(scene::IMesh *src_mesh)
src_mesh->setBoundingBox(bbox);
}
+bool checkMeshNormals(scene::IMesh *mesh)
+{
+ u32 buffer_count = mesh->getMeshBufferCount();
+
+ for (u32 i = 0; i < buffer_count; i++) {
+ scene::IMeshBuffer *buffer = mesh->getMeshBuffer(i);
+
+ // Here we intentionally check only first normal, assuming that if buffer
+ // has it valid, then most likely all other ones are fine too. We can
+ // check all of the normals to have length, but it seems like an overkill
+ // hurting the performance and covering only really weird broken models.
+ f32 length = buffer->getNormal(0).getLength();
+
+ if (!std::isfinite(length) || length < 1e-10f)
+ return false;
+ }
+
+ return true;
+}
+
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
{
switch (mesh_buffer->getVertexType()) {
diff --git a/src/client/mesh.h b/src/client/mesh.h
index 0c4094de2..103c61e45 100644
--- a/src/client/mesh.h
+++ b/src/client/mesh.h
@@ -122,6 +122,12 @@ scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
void recalculateBoundingBox(scene::IMesh *src_mesh);
/*
+ Check if mesh has valid normals and return true if it does.
+ We assume normal to be valid when it's 0 < length < Inf. and not NaN
+ */
+bool checkMeshNormals(scene::IMesh *mesh);
+
+/*
Vertex cache optimization according to the Forsyth paper:
http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
Ported from irrlicht 1.8
diff --git a/src/client/particles.cpp b/src/client/particles.cpp
index a0e4e54eb..7acd996dc 100644
--- a/src/client/particles.cpp
+++ b/src/client/particles.cpp
@@ -37,32 +37,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Utility
*/
-v3f random_v3f(v3f min, v3f max)
+static f32 random_f32(f32 min, f32 max)
+{
+ return rand() / (float)RAND_MAX * (max - min) + min;
+}
+
+static v3f random_v3f(v3f min, v3f max)
{
return v3f(
- rand() / (float)RAND_MAX * (max.X - min.X) + min.X,
- rand() / (float)RAND_MAX * (max.Y - min.Y) + min.Y,
- rand() / (float)RAND_MAX * (max.Z - min.Z) + min.Z);
+ random_f32(min.X, max.X),
+ random_f32(min.Y, max.Y),
+ random_f32(min.Z, max.Z));
}
+/*
+ Particle
+*/
+
Particle::Particle(
IGameDef *gamedef,
LocalPlayer *player,
ClientEnvironment *env,
- v3f pos,
- v3f velocity,
- v3f acceleration,
- float expirationtime,
- float size,
- bool collisiondetection,
- bool collision_removal,
- bool object_collision,
- bool vertical,
+ const ParticleParameters &p,
video::ITexture *texture,
v2f texpos,
v2f texsize,
- const struct TileAnimationParams &anim,
- u8 glow,
video::SColor color
):
scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
@@ -81,33 +80,28 @@ Particle::Particle(
m_material.setTexture(0, texture);
m_texpos = texpos;
m_texsize = texsize;
- m_animation = anim;
+ m_animation = p.animation;
// Color
m_base_color = color;
m_color = color;
// Particle related
- m_pos = pos;
- m_velocity = velocity;
- m_acceleration = acceleration;
- m_expiration = expirationtime;
+ m_pos = p.pos;
+ m_velocity = p.vel;
+ m_acceleration = p.acc;
+ m_expiration = p.expirationtime;
m_player = player;
- m_size = size;
- m_collisiondetection = collisiondetection;
- m_collision_removal = collision_removal;
- m_object_collision = object_collision;
- m_vertical = vertical;
- m_glow = glow;
+ m_size = p.size;
+ m_collisiondetection = p.collisiondetection;
+ m_collision_removal = p.collision_removal;
+ m_object_collision = p.object_collision;
+ m_vertical = p.vertical;
+ m_glow = p.glow;
// Irrlicht stuff
- m_collisionbox = aabb3f(
- -size / 2,
- -size / 2,
- -size / 2,
- size / 2,
- size / 2,
- size / 2);
+ const float c = p.size / 2;
+ m_collisionbox = aabb3f(-c, -c, -c, c, c, c);
this->setAutomaticCulling(scene::EAC_OFF);
// Init lighting
@@ -255,52 +249,22 @@ void Particle::updateVertices()
ParticleSpawner::ParticleSpawner(
IGameDef *gamedef,
LocalPlayer *player,
- u16 amount,
- float time,
- v3f minpos, v3f maxpos,
- v3f minvel, v3f maxvel,
- v3f minacc, v3f maxacc,
- float minexptime, float maxexptime,
- float minsize, float maxsize,
- bool collisiondetection,
- bool collision_removal,
- bool object_collision,
+ const ParticleSpawnerParameters &p,
u16 attached_id,
- bool vertical,
video::ITexture *texture,
- const struct TileAnimationParams &anim,
- u8 glow,
ParticleManager *p_manager
):
- m_particlemanager(p_manager)
+ m_particlemanager(p_manager), p(p)
{
m_gamedef = gamedef;
m_player = player;
- m_amount = amount;
- m_spawntime = time;
- m_minpos = minpos;
- m_maxpos = maxpos;
- m_minvel = minvel;
- m_maxvel = maxvel;
- m_minacc = minacc;
- m_maxacc = maxacc;
- m_minexptime = minexptime;
- m_maxexptime = maxexptime;
- m_minsize = minsize;
- m_maxsize = maxsize;
- m_collisiondetection = collisiondetection;
- m_collision_removal = collision_removal;
- m_object_collision = object_collision;
m_attached_id = attached_id;
- m_vertical = vertical;
m_texture = texture;
m_time = 0;
- m_animation = anim;
- m_glow = glow;
- for (u16 i = 0; i <= m_amount; i++)
- {
- float spawntime = (float)rand() / (float)RAND_MAX * m_spawntime;
+ m_spawntimes.reserve(p.amount + 1);
+ for (u16 i = 0; i <= p.amount; i++) {
+ float spawntime = rand() / (float)RAND_MAX * p.time;
m_spawntimes.push_back(spawntime);
}
}
@@ -309,7 +273,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
const core::matrix4 *attached_absolute_pos_rot_matrix)
{
v3f ppos = m_player->getPosition() / BS;
- v3f pos = random_v3f(m_minpos, m_maxpos);
+ v3f pos = random_v3f(p.minpos, p.maxpos);
// Need to apply this first or the following check
// will be wrong for attached spawners
@@ -326,41 +290,51 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
if (pos.getDistanceFrom(ppos) > radius)
return;
- v3f vel = random_v3f(m_minvel, m_maxvel);
- v3f acc = random_v3f(m_minacc, m_maxacc);
+ // Parameters for the single particle we're about to spawn
+ ParticleParameters pp;
+ pp.pos = pos;
+
+ pp.vel = random_v3f(p.minvel, p.maxvel);
+ pp.acc = random_v3f(p.minacc, p.maxacc);
if (attached_absolute_pos_rot_matrix) {
// Apply attachment rotation
- attached_absolute_pos_rot_matrix->rotateVect(vel);
- attached_absolute_pos_rot_matrix->rotateVect(acc);
+ attached_absolute_pos_rot_matrix->rotateVect(pp.vel);
+ attached_absolute_pos_rot_matrix->rotateVect(pp.acc);
}
- float exptime = rand() / (float)RAND_MAX
- * (m_maxexptime - m_minexptime)
- + m_minexptime;
+ pp.expirationtime = random_f32(p.minexptime, p.maxexptime);
+ p.copyCommon(pp);
+
+ video::ITexture *texture;
+ v2f texpos, texsize;
+ video::SColor color(0xFFFFFFFF);
+
+ if (p.node.getContent() != CONTENT_IGNORE) {
+ const ContentFeatures &f =
+ m_particlemanager->m_env->getGameDef()->ndef()->get(p.node);
+ if (!ParticleManager::getNodeParticleParams(p.node, f, pp, &texture,
+ texpos, texsize, &color, p.node_tile))
+ return;
+ } else {
+ texture = m_texture;
+ texpos = v2f(0.0f, 0.0f);
+ texsize = v2f(1.0f, 1.0f);
+ }
- float size = rand() / (float)RAND_MAX
- * (m_maxsize - m_minsize)
- + m_minsize;
+ // Allow keeping default random size
+ if (p.maxsize > 0.0f)
+ pp.size = random_f32(p.minsize, p.maxsize);
m_particlemanager->addParticle(new Particle(
m_gamedef,
m_player,
env,
- pos,
- vel,
- acc,
- exptime,
- size,
- m_collisiondetection,
- m_collision_removal,
- m_object_collision,
- m_vertical,
- m_texture,
- v2f(0.0, 0.0),
- v2f(1.0, 1.0),
- m_animation,
- m_glow
+ pp,
+ texture,
+ texpos,
+ texsize,
+ color
));
}
@@ -375,18 +349,17 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env)
const core::matrix4 *attached_absolute_pos_rot_matrix = nullptr;
if (m_attached_id) {
if (GenericCAO *attached = dynamic_cast<GenericCAO *>(env->getActiveObject(m_attached_id))) {
- attached_absolute_pos_rot_matrix = &attached->getAbsolutePosRotMatrix();
+ attached_absolute_pos_rot_matrix = attached->getAbsolutePosRotMatrix();
} else {
unloaded = true;
}
}
- if (m_spawntime != 0) {
+ if (p.time != 0) {
// Spawner exists for a predefined timespan
- for (std::vector<float>::iterator i = m_spawntimes.begin();
- i != m_spawntimes.end();) {
- if ((*i) <= m_time && m_amount > 0) {
- --m_amount;
+ for (auto i = m_spawntimes.begin(); i != m_spawntimes.end(); ) {
+ if ((*i) <= m_time && p.amount > 0) {
+ --p.amount;
// Pretend to, but don't actually spawn a particle if it is
// attached to an unloaded object or distant from player.
@@ -405,13 +378,16 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env)
if (unloaded)
return;
- for (int i = 0; i <= m_amount; i++) {
+ for (int i = 0; i <= p.amount; i++) {
if (rand() / (float)RAND_MAX < dtime)
spawnParticle(env, radius, attached_absolute_pos_rot_matrix);
}
}
}
+/*
+ ParticleManager
+*/
ParticleManager::ParticleManager(ClientEnvironment *env) :
m_env(env)
@@ -479,99 +455,106 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
{
switch (event->type) {
case CE_DELETE_PARTICLESPAWNER: {
- MutexAutoLock lock(m_spawner_list_lock);
- if (m_particle_spawners.find(event->delete_particlespawner.id) !=
- m_particle_spawners.end()) {
- delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
- m_particle_spawners.erase(event->delete_particlespawner.id);
- }
+ deleteParticleSpawner(event->delete_particlespawner.id);
// no allocated memory in delete event
break;
}
case CE_ADD_PARTICLESPAWNER: {
- {
- MutexAutoLock lock(m_spawner_list_lock);
- if (m_particle_spawners.find(event->add_particlespawner.id) !=
- m_particle_spawners.end()) {
- delete m_particle_spawners.find(event->add_particlespawner.id)->second;
- m_particle_spawners.erase(event->add_particlespawner.id);
- }
- }
+ deleteParticleSpawner(event->add_particlespawner.id);
+
+ const ParticleSpawnerParameters &p = *event->add_particlespawner.p;
video::ITexture *texture =
- client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
+ client->tsrc()->getTextureForMesh(p.texture);
auto toadd = new ParticleSpawner(client, player,
- event->add_particlespawner.amount,
- event->add_particlespawner.spawntime,
- *event->add_particlespawner.minpos,
- *event->add_particlespawner.maxpos,
- *event->add_particlespawner.minvel,
- *event->add_particlespawner.maxvel,
- *event->add_particlespawner.minacc,
- *event->add_particlespawner.maxacc,
- event->add_particlespawner.minexptime,
- event->add_particlespawner.maxexptime,
- event->add_particlespawner.minsize,
- event->add_particlespawner.maxsize,
- event->add_particlespawner.collisiondetection,
- event->add_particlespawner.collision_removal,
- event->add_particlespawner.object_collision,
+ p,
event->add_particlespawner.attached_id,
- event->add_particlespawner.vertical,
texture,
- event->add_particlespawner.animation,
- event->add_particlespawner.glow,
this);
- /* delete allocated content of event */
- delete event->add_particlespawner.minpos;
- delete event->add_particlespawner.maxpos;
- delete event->add_particlespawner.minvel;
- delete event->add_particlespawner.maxvel;
- delete event->add_particlespawner.minacc;
- delete event->add_particlespawner.texture;
- delete event->add_particlespawner.maxacc;
-
- {
- MutexAutoLock lock(m_spawner_list_lock);
- m_particle_spawners[event->add_particlespawner.id] = toadd;
- }
+ addParticleSpawner(event->add_particlespawner.id, toadd);
+
+ delete event->add_particlespawner.p;
break;
}
case CE_SPAWN_PARTICLE: {
- video::ITexture *texture =
- client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
-
- Particle *toadd = new Particle(client, player, m_env,
- *event->spawn_particle.pos,
- *event->spawn_particle.vel,
- *event->spawn_particle.acc,
- event->spawn_particle.expirationtime,
- event->spawn_particle.size,
- event->spawn_particle.collisiondetection,
- event->spawn_particle.collision_removal,
- event->spawn_particle.object_collision,
- event->spawn_particle.vertical,
- texture,
- v2f(0.0, 0.0),
- v2f(1.0, 1.0),
- event->spawn_particle.animation,
- event->spawn_particle.glow);
+ ParticleParameters &p = *event->spawn_particle;
- addParticle(toadd);
+ video::ITexture *texture;
+ v2f texpos, texsize;
+ video::SColor color(0xFFFFFFFF);
- delete event->spawn_particle.pos;
- delete event->spawn_particle.vel;
- delete event->spawn_particle.acc;
- delete event->spawn_particle.texture;
+ f32 oldsize = p.size;
+
+ if (p.node.getContent() != CONTENT_IGNORE) {
+ const ContentFeatures &f = m_env->getGameDef()->ndef()->get(p.node);
+ if (!getNodeParticleParams(p.node, f, p, &texture, texpos,
+ texsize, &color, p.node_tile))
+ texture = nullptr;
+ } else {
+ texture = client->tsrc()->getTextureForMesh(p.texture);
+ texpos = v2f(0.0f, 0.0f);
+ texsize = v2f(1.0f, 1.0f);
+ }
+ // Allow keeping default random size
+ if (oldsize > 0.0f)
+ p.size = oldsize;
+
+ if (texture) {
+ Particle *toadd = new Particle(client, player, m_env,
+ p, texture, texpos, texsize, color);
+
+ addParticle(toadd);
+ }
+
+ delete event->spawn_particle;
break;
}
default: break;
}
}
+bool ParticleManager::getNodeParticleParams(const MapNode &n,
+ const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture,
+ v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum)
+{
+ // No particles for "airlike" nodes
+ if (f.drawtype == NDT_AIRLIKE)
+ return false;
+
+ // Texture
+ u8 texid;
+ if (tilenum > 0 && tilenum <= 6)
+ texid = tilenum - 1;
+ else
+ texid = rand() % 6;
+ const TileLayer &tile = f.tiles[texid].layers[0];
+ p.animation.type = TAT_NONE;
+
+ // Only use first frame of animated texture
+ if (tile.material_flags & MATERIAL_FLAG_ANIMATION)
+ *texture = (*tile.frames)[0].texture;
+ else
+ *texture = tile.texture;
+
+ float size = (rand() % 8) / 64.0f;
+ p.size = BS * size;
+ if (tile.scale)
+ size /= tile.scale;
+ texsize = v2f(size * 2.0f, size * 2.0f);
+ texpos.X = (rand() % 64) / 64.0f - texsize.X;
+ texpos.Y = (rand() % 64) / 64.0f - texsize.Y;
+
+ if (tile.has_color)
+ *color = tile.color;
+ else
+ n.getColor(f, color);
+
+ return true;
+}
+
// The final burst of particles when a node is finally dug, *not* particles
// spawned during the digging of a node.
@@ -593,73 +576,41 @@ void ParticleManager::addDiggingParticles(IGameDef *gamedef,
void ParticleManager::addNodeParticle(IGameDef *gamedef,
LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f)
{
- // No particles for "airlike" nodes
- if (f.drawtype == NDT_AIRLIKE)
- return;
-
- // Texture
- u8 texid = myrand_range(0, 5);
- const TileLayer &tile = f.tiles[texid].layers[0];
+ ParticleParameters p;
video::ITexture *texture;
- struct TileAnimationParams anim;
- anim.type = TAT_NONE;
+ v2f texpos, texsize;
+ video::SColor color;
- // Only use first frame of animated texture
- if (tile.material_flags & MATERIAL_FLAG_ANIMATION)
- texture = (*tile.frames)[0].texture;
- else
- texture = tile.texture;
+ if (!getNodeParticleParams(n, f, p, &texture, texpos, texsize, &color))
+ return;
- float size = (rand() % 8) / 64.0f;
- float visual_size = BS * size;
- if (tile.scale)
- size /= tile.scale;
- v2f texsize(size * 2.0f, size * 2.0f);
- v2f texpos;
- texpos.X = (rand() % 64) / 64.0f - texsize.X;
- texpos.Y = (rand() % 64) / 64.0f - texsize.Y;
+ p.expirationtime = (rand() % 100) / 100.0f;
// Physics
- v3f velocity(
+ p.vel = v3f(
(rand() % 150) / 50.0f - 1.5f,
(rand() % 150) / 50.0f,
(rand() % 150) / 50.0f - 1.5f
);
- v3f acceleration(
+ p.acc = v3f(
0.0f,
-player->movement_gravity * player->physics_override_gravity / BS,
0.0f
);
- v3f particlepos = v3f(
+ p.pos = v3f(
(f32)pos.X + (rand() % 100) / 200.0f - 0.25f,
(f32)pos.Y + (rand() % 100) / 200.0f - 0.25f,
(f32)pos.Z + (rand() % 100) / 200.0f - 0.25f
);
- video::SColor color;
- if (tile.has_color)
- color = tile.color;
- else
- n.getColor(f, &color);
-
Particle *toadd = new Particle(
gamedef,
player,
m_env,
- particlepos,
- velocity,
- acceleration,
- (rand() % 100) / 100.0f, // expiration time
- visual_size,
- true,
- false,
- false,
- false,
+ p,
texture,
texpos,
texsize,
- anim,
- 0,
color);
addParticle(toadd);
@@ -670,3 +621,20 @@ void ParticleManager::addParticle(Particle *toadd)
MutexAutoLock lock(m_particle_list_lock);
m_particles.push_back(toadd);
}
+
+
+void ParticleManager::addParticleSpawner(u64 id, ParticleSpawner *toadd)
+{
+ MutexAutoLock lock(m_spawner_list_lock);
+ m_particle_spawners[id] = toadd;
+}
+
+void ParticleManager::deleteParticleSpawner(u64 id)
+{
+ MutexAutoLock lock(m_spawner_list_lock);
+ auto it = m_particle_spawners.find(id);
+ if (it != m_particle_spawners.end()) {
+ delete it->second;
+ m_particle_spawners.erase(it);
+ }
+}
diff --git a/src/client/particles.h b/src/client/particles.h
index e7b8cbe24..2011f0262 100644
--- a/src/client/particles.h
+++ b/src/client/particles.h
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "client/tile.h"
#include "localplayer.h"
-#include "tileanimation.h"
+#include "../particles.h"
struct ClientEvent;
class ParticleManager;
@@ -38,21 +38,11 @@ class Particle : public scene::ISceneNode
IGameDef* gamedef,
LocalPlayer *player,
ClientEnvironment *env,
- v3f pos,
- v3f velocity,
- v3f acceleration,
- float expirationtime,
- float size,
- bool collisiondetection,
- bool collision_removal,
- bool object_collision,
- bool vertical,
+ const ParticleParameters &p,
video::ITexture *texture,
v2f texpos,
v2f texsize,
- const struct TileAnimationParams &anim,
- u8 glow,
- video::SColor color = video::SColor(0xFFFFFFFF)
+ video::SColor color
);
~Particle() = default;
@@ -119,20 +109,9 @@ class ParticleSpawner
public:
ParticleSpawner(IGameDef* gamedef,
LocalPlayer *player,
- u16 amount,
- float time,
- v3f minp, v3f maxp,
- v3f minvel, v3f maxvel,
- v3f minacc, v3f maxacc,
- float minexptime, float maxexptime,
- float minsize, float maxsize,
- bool collisiondetection,
- bool collision_removal,
- bool object_collision,
+ const ParticleSpawnerParameters &p,
u16 attached_id,
- bool vertical,
video::ITexture *texture,
- const struct TileAnimationParams &anim, u8 glow,
ParticleManager* p_manager);
~ParticleSpawner() = default;
@@ -140,7 +119,7 @@ public:
void step(float dtime, ClientEnvironment *env);
bool get_expired ()
- { return (m_amount <= 0) && m_spawntime != 0; }
+ { return p.amount <= 0 && p.time != 0; }
private:
void spawnParticle(ClientEnvironment *env, float radius,
@@ -150,27 +129,10 @@ private:
float m_time;
IGameDef *m_gamedef;
LocalPlayer *m_player;
- u16 m_amount;
- float m_spawntime;
- v3f m_minpos;
- v3f m_maxpos;
- v3f m_minvel;
- v3f m_maxvel;
- v3f m_minacc;
- v3f m_maxacc;
- float m_minexptime;
- float m_maxexptime;
- float m_minsize;
- float m_maxsize;
+ ParticleSpawnerParameters p;
video::ITexture *m_texture;
std::vector<float> m_spawntimes;
- bool m_collisiondetection;
- bool m_collision_removal;
- bool m_object_collision;
- bool m_vertical;
u16 m_attached_id;
- struct TileAnimationParams m_animation;
- u8 m_glow;
};
/**
@@ -197,8 +159,8 @@ public:
/**
* This function is only used by client particle spawners
*
- * We don't need to check the particle spawner list because client ID will n
- * ever overlap (u64)
+ * We don't need to check the particle spawner list because client ID will
+ * never overlap (u64)
* @return new id
*/
u64 generateSpawnerId()
@@ -207,9 +169,15 @@ public:
}
protected:
+ static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f,
+ ParticleParameters &p, video::ITexture **texture, v2f &texpos,
+ v2f &texsize, video::SColor *color, u8 tilenum = 0);
+
void addParticle(Particle* toadd);
private:
+ void addParticleSpawner(u64 id, ParticleSpawner *toadd);
+ void deleteParticleSpawner(u64 id);
void stepParticles(float dtime);
void stepSpawners(float dtime);
diff --git a/src/client/render/factory.h b/src/client/render/factory.h
index 22738404a..e3339a836 100644
--- a/src/client/render/factory.h
+++ b/src/client/render/factory.h
@@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#pragma once
+
#include <string>
#include "core.h"
diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp
index 6e6509eeb..f5aca8f58 100644
--- a/src/client/renderingengine.cpp
+++ b/src/client/renderingengine.cpp
@@ -45,7 +45,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
+#endif
+#ifdef _WIN32
+#include <windows.h>
+#include <winuser.h>
#endif
#if ENABLE_GLES
@@ -126,12 +130,9 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
params.ZBufferBits = 24;
#ifdef __ANDROID__
- // clang-format off
params.PrivateData = porting::app_global;
- params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM + "media" +
- DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
- // clang-format on
-#elif ENABLE_GLES
+#endif
+#if ENABLE_GLES
// there is no standardized path for these on desktop
std::string rel_path = std::string("client") + DIR_DELIM
+ "shaders" + DIR_DELIM + "Irrlicht";
@@ -226,27 +227,17 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name)
{
// FIXME: It would make more sense for there to be a switch of some
// sort here that would call the correct toplevel setup methods for
- // the environment Minetest is running in but for now not deviating
- // from the original pattern.
+ // the environment Minetest is running in.
/* Setting Xorg properties for the top level window */
setupTopLevelXorgWindow(name);
- /* Done with Xorg properties */
/* Setting general properties for the top level window */
verbosestream << "Client: Configuring general top level"
<< " window properties"
<< std::endl;
-
bool result = setWindowIcon();
- verbosestream << "Client: Finished configuring general top level"
- << " window properties"
- << std::endl;
- /* Done with general properties */
-
- // FIXME: setWindowIcon returns a bool result but it is unused.
- // For now continue to return this result.
return result;
}
@@ -262,7 +253,7 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name)
return;
}
- verbosestream << "Client: Configuring Xorg specific top level"
+ verbosestream << "Client: Configuring X11-specific top level"
<< " window properties"
<< std::endl;
@@ -309,8 +300,6 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name)
Atom NET_WM_PID = XInternAtom(x11_dpl, "_NET_WM_PID", false);
pid_t pid = getpid();
- infostream << "Client: PID is '" << static_cast<long>(pid) << "'"
- << std::endl;
XChangeProperty(x11_dpl, x11_win, NET_WM_PID,
XA_CARDINAL, 32, PropModeReplace,
@@ -327,13 +316,31 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name)
XChangeProperty (x11_dpl, x11_win, WM_CLIENT_LEADER,
XA_WINDOW, 32, PropModeReplace,
reinterpret_cast<unsigned char *>(&x11_win), 1);
-
- verbosestream << "Client: Finished configuring Xorg specific top level"
- << " window properties"
- << std::endl;
#endif
}
+#ifdef _WIN32
+static bool getWindowHandle(irr::video::IVideoDriver *driver, HWND &hWnd)
+{
+ const video::SExposedVideoData exposedData = driver->getExposedVideoData();
+
+ switch (driver->getDriverType()) {
+ case video::EDT_DIRECT3D8:
+ hWnd = reinterpret_cast<HWND>(exposedData.D3D8.HWnd);
+ break;
+ case video::EDT_DIRECT3D9:
+ hWnd = reinterpret_cast<HWND>(exposedData.D3D9.HWnd);
+ break;
+ case video::EDT_OPENGL:
+ hWnd = reinterpret_cast<HWND>(exposedData.OpenGLWin32.HWnd);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+#endif
bool RenderingEngine::setWindowIcon()
{
@@ -351,22 +358,9 @@ bool RenderingEngine::setWindowIcon()
"-xorg-icon-128.png");
#endif
#elif defined(_WIN32)
- const video::SExposedVideoData exposedData = driver->getExposedVideoData();
HWND hWnd; // Window handle
-
- switch (driver->getDriverType()) {
- case video::EDT_DIRECT3D8:
- hWnd = reinterpret_cast<HWND>(exposedData.D3D8.HWnd);
- break;
- case video::EDT_DIRECT3D9:
- hWnd = reinterpret_cast<HWND>(exposedData.D3D9.HWnd);
- break;
- case video::EDT_OPENGL:
- hWnd = reinterpret_cast<HWND>(exposedData.OpenGLWin32.HWnd);
- break;
- default:
+ if (!getWindowHandle(driver, hWnd))
return false;
- }
// Load the ICON from resource file
const HICON hicon = LoadIcon(GetModuleHandle(NULL),
@@ -648,7 +642,7 @@ const char *RenderingEngine::getVideoDriverFriendlyName(irr::video::E_DRIVER_TYP
}
#ifndef __ANDROID__
-#ifdef XORG_USED
+#if defined(XORG_USED)
static float calcDisplayDensity()
{
@@ -683,12 +677,42 @@ float RenderingEngine::getDisplayDensity()
return cached_display_density;
}
-#else // XORG_USED
+#elif defined(_WIN32)
+
+
+static float calcDisplayDensity(irr::video::IVideoDriver *driver)
+{
+ HWND hWnd;
+ if (getWindowHandle(driver, hWnd)) {
+ HDC hdc = GetDC(hWnd);
+ float dpi = GetDeviceCaps(hdc, LOGPIXELSX);
+ ReleaseDC(hWnd, hdc);
+ return dpi / 96.0f;
+ }
+
+ /* return manually specified dpi */
+ return g_settings->getFloat("screen_dpi") / 96.0f;
+}
+
+float RenderingEngine::getDisplayDensity()
+{
+ static bool cached = false;
+ static float display_density;
+ if (!cached) {
+ display_density = calcDisplayDensity(get_video_driver());
+ cached = true;
+ }
+ return display_density;
+}
+
+#else
+
float RenderingEngine::getDisplayDensity()
{
return g_settings->getFloat("screen_dpi") / 96.0;
}
-#endif // XORG_USED
+
+#endif
v2u32 RenderingEngine::getDisplaySize()
{
diff --git a/src/client/shader.cpp b/src/client/shader.cpp
index eda415ce6..ee6079f7a 100644
--- a/src/client/shader.cpp
+++ b/src/client/shader.cpp
@@ -537,11 +537,13 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
shaderinfo.base_material = video::EMT_SOLID;
break;
case TILE_MATERIAL_ALPHA:
+ case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_BASIC:
+ case TILE_MATERIAL_PLAIN:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
case TILE_MATERIAL_WAVING_LIQUID_BASIC:
@@ -644,9 +646,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
"TILE_MATERIAL_WAVING_LIQUID_BASIC",
"TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT",
"TILE_MATERIAL_WAVING_LIQUID_OPAQUE",
+ "TILE_MATERIAL_PLAIN",
+ "TILE_MATERIAL_PLAIN_ALPHA",
};
- for (int i = 0; i < 10; i++){
+ for (int i = 0; i < 12; i++){
shaders_header += "#define ";
shaders_header += materialTypes[i];
shaders_header += " ";
diff --git a/src/client/sky.cpp b/src/client/sky.cpp
index 7a7b188ce..2e0cbca86 100644
--- a/src/client/sky.cpp
+++ b/src/client/sky.cpp
@@ -252,35 +252,10 @@ void Sky::render()
if (m_visible) {
driver->setMaterial(m_materials[1]);
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);
- }
-
- // 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);
+ vertices[0] = video::S3DVertex(-1, -0.02, -1, 0, 0, 1, m_bgcolor, t, t);
+ vertices[1] = video::S3DVertex( 1, -0.02, -1, 0, 0, 1, m_bgcolor, o, t);
+ vertices[2] = video::S3DVertex( 1, 0.45, -1, 0, 0, 1, m_skycolor, o, o);
+ vertices[3] = video::S3DVertex(-1, 0.45, -1, 0, 0, 1, m_skycolor, t, o);
for (video::S3DVertex &vertex : vertices) {
if (j == 0)
// Don't switch
@@ -529,7 +504,7 @@ void Sky::update(float time_of_day, float time_brightness,
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;
+ pointcolor_sun_f = m_sky_params.fog_sun_tint;
} else {
pointcolor_sun_f.r = pointcolor_light * 1;
pointcolor_sun_f.b = pointcolor_light *
@@ -548,9 +523,9 @@ void Sky::update(float time_of_day, float time_brightness,
);
} 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,
+ (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light,
+ (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light,
+ (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light,
1
);
}
@@ -932,17 +907,17 @@ void Sky::setStarCount(u16 star_count, bool force_update)
}
}
-void Sky::setSkyColors(const SkyboxParams sky)
+void Sky::setSkyColors(const SkyColor &sky_color)
{
- m_sky_params.sky_color = sky.sky_color;
+ m_sky_params.sky_color = 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;
+ m_sky_params.fog_sun_tint = sun_tint;
+ m_sky_params.fog_moon_tint = moon_tint;
// Faster than comparing strings every rendering frame
if (use_sun_tint == "default")
m_default_tint = true;
diff --git a/src/client/sky.h b/src/client/sky.h
index 8637f96d4..3227e8f59 100644
--- a/src/client/sky.h
+++ b/src/client/sky.h
@@ -94,7 +94,7 @@ public:
m_bgcolor = bgcolor;
m_skycolor = skycolor;
}
- void setSkyColors(const SkyboxParams sky);
+ void setSkyColors(const SkyColor &sky_color);
void setHorizonTint(video::SColor sun_tint, video::SColor moon_tint,
std::string use_sun_tint);
void setInClouds(bool clouds) { m_in_clouds = clouds; }
diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp
index 8e696f302..20a651c1d 100644
--- a/src/client/sound_openal.cpp
+++ b/src/client/sound_openal.cpp
@@ -165,8 +165,8 @@ SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
<< "preparing sound buffer" << std::endl;
}
- infostream << "Audio file "
- << filename_for_logging << " loaded" << std::endl;
+ //infostream << "Audio file "
+ // << filename_for_logging << " loaded" << std::endl;
// Clean up!
ov_clear(oggFile);
@@ -275,25 +275,38 @@ public:
m_device(nullptr, delete_alcdevice),
m_context(nullptr, delete_alccontext)
{
- if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice)))
- throw std::runtime_error("Audio: Global Initialization: Device Open");
+ }
+
+ bool init()
+ {
+ if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) {
+ errorstream << "Audio: Global Initialization: Failed to open device" << std::endl;
+ return false;
+ }
if (!(m_context = unique_ptr_alccontext(
alcCreateContext(m_device.get(), nullptr), delete_alccontext))) {
- throw std::runtime_error("Audio: Global Initialization: Context Create");
+ errorstream << "Audio: Global Initialization: Failed to create context" << std::endl;
+ return false;
}
- if (!alcMakeContextCurrent(m_context.get()))
- throw std::runtime_error("Audio: Global Initialization: Context Current");
+ if (!alcMakeContextCurrent(m_context.get())) {
+ errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl;
+ return false;
+ }
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
- if (alGetError() != AL_NO_ERROR)
- throw std::runtime_error("Audio: Global Initialization: OpenAL Error");
+ if (alGetError() != AL_NO_ERROR) {
+ errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl;
+ return false;
+ }
infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
<< ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
<< std::endl;
+
+ return true;
}
~SoundManagerSingleton()
@@ -498,9 +511,11 @@ public:
// Remove stopped sounds
void maintain()
{
- verbosestream<<"OpenALSoundManager::maintain(): "
- <<m_sounds_playing.size()<<" playing sounds, "
- <<m_buffers.size()<<" sound names loaded"<<std::endl;
+ if (!m_sounds_playing.empty()) {
+ verbosestream << "OpenALSoundManager::maintain(): "
+ << m_sounds_playing.size() <<" playing sounds, "
+ << m_buffers.size() <<" sound names loaded"<<std::endl;
+ }
std::unordered_set<int> del_list;
for (const auto &sp : m_sounds_playing) {
int id = sp.first;
@@ -530,7 +545,7 @@ public:
SoundBuffer *buf = load_ogg_from_file(filepath);
if (buf)
addBuffer(name, buf);
- return false;
+ return !!buf;
}
bool loadSoundData(const std::string &name,
@@ -539,7 +554,7 @@ public:
SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
if (buf)
addBuffer(name, buf);
- return false;
+ return !!buf;
}
void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
@@ -680,7 +695,11 @@ public:
std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
{
- return std::shared_ptr<SoundManagerSingleton>(new SoundManagerSingleton());
+ auto smg = std::make_shared<SoundManagerSingleton>();
+ if (!smg->init()) {
+ smg.reset();
+ }
+ return smg;
}
ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index 3189ab28c..d03588b2b 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -471,8 +471,8 @@ TextureSource::~TextureSource()
driver->removeTexture(t);
}
- infostream << "~TextureSource() "<< textures_before << "/"
- << driver->getTextureCount() << std::endl;
+ infostream << "~TextureSource() before cleanup: "<< textures_before
+ << " after: " << driver->getTextureCount() << std::endl;
}
u32 TextureSource::getTextureId(const std::string &name)
@@ -668,7 +668,14 @@ video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *id)
{
- return getTexture(name + "^[applyfiltersformesh", id);
+ static thread_local bool filter_needed =
+ g_settings->getBool("texture_clean_transparent") ||
+ ((m_setting_trilinear_filter || m_setting_bilinear_filter) &&
+ g_settings->getS32("texture_min_size") > 1);
+ // Avoid duplicating texture if it won't actually change
+ if (filter_needed)
+ return getTexture(name + "^[applyfiltersformesh", id);
+ return getTexture(name, id);
}
Palette* TextureSource::getPalette(const std::string &name)
@@ -763,6 +770,9 @@ void TextureSource::rebuildImagesAndTextures()
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
sanity_check(driver);
+ infostream << "TextureSource: recreating " << m_textureinfo_cache.size()
+ << " textures" << std::endl;
+
// Recreate textures
for (TextureInfo &ti : m_textureinfo_cache) {
video::IImage *img = generateImage(ti.name);
@@ -1270,8 +1280,6 @@ bool TextureSource::generateImagePart(std::string part_of_name,
video::IImage *img = generateImage(filename);
if (img) {
core::dimension2d<u32> dim = img->getDimension();
- infostream<<"Size "<<dim.Width
- <<"x"<<dim.Height<<std::endl;
core::position2d<s32> pos_base(x, y);
video::IImage *img2 =
driver->createImage(video::ECF_A8R8G8B8, dim);
@@ -1622,6 +1630,9 @@ bool TextureSource::generateImagePart(std::string part_of_name,
*/
else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
{
+ /* IMPORTANT: When changing this, getTextureForMesh() needs to be
+ * updated too. */
+
// Apply the "clean transparent" filter, if configured.
if (g_settings->getBool("texture_clean_transparent"))
imageCleanTransparent(baseimg, 127);
diff --git a/src/client/tile.h b/src/client/tile.h
index 533df676e..52e0a2b2b 100644
--- a/src/client/tile.h
+++ b/src/client/tile.h
@@ -150,6 +150,8 @@ enum MaterialType{
TILE_MATERIAL_WAVING_LIQUID_BASIC,
TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT,
TILE_MATERIAL_WAVING_LIQUID_OPAQUE,
+ TILE_MATERIAL_PLAIN,
+ TILE_MATERIAL_PLAIN_ALPHA
};
// Material flags
diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp
index 2c6807fab..8cd3e29a9 100644
--- a/src/client/wieldmesh.cpp
+++ b/src/client/wieldmesh.cpp
@@ -347,7 +347,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
scene::SMesh *mesh = nullptr;
if (m_enable_shaders) {
- u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
+ u32 shader_id = shdrsrc->getShader("object_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
m_material_type = shdrsrc->getShaderInfo(shader_id).material;
}
@@ -467,10 +467,29 @@ void WieldMeshSceneNode::setColor(video::SColor c)
bc.getGreen() * green / 255,
bc.getBlue() * blue / 255);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- colorizeMeshBuffer(buf, &buffercolor);
+
+ if (m_enable_shaders)
+ setMeshBufferColor(buf, buffercolor);
+ else
+ colorizeMeshBuffer(buf, &buffercolor);
}
}
+void WieldMeshSceneNode::setNodeLightColor(video::SColor color)
+{
+ if (!m_meshnode)
+ return;
+
+ if (m_enable_shaders) {
+ for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
+ video::SMaterial &material = m_meshnode->getMaterial(i);
+ material.EmissiveColor = color;
+ }
+ }
+
+ setColor(color);
+}
+
void WieldMeshSceneNode::render()
{
// note: if this method is changed to actually do something,
diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h
index 7c80a811b..933097230 100644
--- a/src/client/wieldmesh.h
+++ b/src/client/wieldmesh.h
@@ -87,6 +87,8 @@ public:
// Must only be used if the constructor was called with lighting = false
void setColor(video::SColor color);
+ void setNodeLightColor(video::SColor color);
+
scene::IMesh *getMesh() { return m_meshnode->getMesh(); }
virtual void render();