Browse Source

VBO-related optimizations and improvements (#14395)

sfan5 2 months ago
parent
commit
bb7f57b095

+ 17 - 0
client/shaders/cloud_shader/opengl_fragment.glsl

@@ -0,0 +1,17 @@
+uniform vec4 fogColor;
+uniform float fogDistance;
+uniform float fogShadingParameter;
+varying vec3 eyeVec;
+
+varying lowp vec4 varColor;
+
+void main(void)
+{
+	vec4 col = varColor;
+
+	float clarity = clamp(fogShadingParameter
+		- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
+	col.rgb = mix(fogColor.rgb, col.rgb, clarity);
+
+	gl_FragColor = col;
+}

+ 21 - 0
client/shaders/cloud_shader/opengl_vertex.glsl

@@ -0,0 +1,21 @@
+uniform vec4 emissiveColor;
+
+varying lowp vec4 varColor;
+
+varying vec3 eyeVec;
+
+void main(void)
+{
+	gl_Position = mWorldViewProj * inVertexPosition;
+
+#ifdef GL_ES
+	vec4 color = inVertexColor.bgra;
+#else
+	vec4 color = inVertexColor;
+#endif
+
+	color *= emissiveColor;
+	varColor = color;
+
+	eyeVec = -(mWorldView * inVertexPosition).xyz;
+}

+ 2 - 2
client/shaders/stars_shader/opengl_fragment.glsl

@@ -1,6 +1,6 @@
-uniform vec4 starColor;
+uniform vec4 emissiveColor;
 
 void main(void)
 {
-	gl_FragColor = starColor;
+	gl_FragColor = emissiveColor;
 }

+ 0 - 49
src/client/clientenvironment.cpp

@@ -39,53 +39,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <algorithm>
 #include "client/renderingengine.h"
 
-/*
-	CAOShaderConstantSetter
-*/
-
-//! Shader constant setter for passing material emissive color to the CAO object_shader
-class CAOShaderConstantSetter : public IShaderConstantSetter
-{
-public:
-	~CAOShaderConstantSetter() override = default;
-
-	void onSetConstants(video::IMaterialRendererServices *services) override
-	{
-		// Ambient color
-		video::SColorf emissive_color(m_emissive_color);
-
-		float as_array[4] = {
-			emissive_color.r,
-			emissive_color.g,
-			emissive_color.b,
-			emissive_color.a,
-		};
-		m_emissive_color_setting.set(as_array, services);
-	}
-
-	void onSetMaterial(const video::SMaterial& material) override
-	{
-		m_emissive_color = material.EmissiveColor;
-	}
-
-private:
-	video::SColor m_emissive_color;
-	CachedPixelShaderSetting<float, 4>
-		m_emissive_color_setting{"emissiveColor"};
-};
-
-class CAOShaderConstantSetterFactory : public IShaderConstantSetterFactory
-{
-public:
-	CAOShaderConstantSetterFactory()
-	{}
-
-	virtual IShaderConstantSetter* create()
-	{
-		return new CAOShaderConstantSetter();
-	}
-};
-
 /*
 	ClientEnvironment
 */
@@ -97,8 +50,6 @@ ClientEnvironment::ClientEnvironment(ClientMap *map,
 	m_texturesource(texturesource),
 	m_client(client)
 {
-	auto *shdrsrc = m_client->getShaderSource();
-	shdrsrc->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory());
 }
 
 ClientEnvironment::~ClientEnvironment()

+ 53 - 53
src/client/clientlauncher.cpp

@@ -107,7 +107,7 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 		return false;
 	}
 
-	if (m_rendering_engine->get_video_driver() == NULL) {
+	if (!m_rendering_engine->get_video_driver()) {
 		errorstream << "Could not initialize video driver." << std::endl;
 		return false;
 	}
@@ -125,51 +125,16 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 		setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
 
 	guienv = m_rendering_engine->get_gui_env();
-	skin = guienv->getSkin();
-	skin->setColor(gui::EGDC_WINDOW_SYMBOL, video::SColor(255, 255, 255, 255));
-	skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
-	skin->setColor(gui::EGDC_3D_LIGHT, video::SColor(0, 0, 0, 0));
-	skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 30, 30, 30));
-	skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
-	skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 120, 50));
-	skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
-
-	float density = rangelim(g_settings->getFloat("gui_scaling"), 0.5, 20) *
-		RenderingEngine::getDisplayDensity();
-	skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density));
-	skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(14.0f * density));
-	skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density));
-	if (density > 1.5f) {
-		std::string sprite_path = porting::path_share + "/textures/base/pack/";
-		if (density > 3.5f)
-			sprite_path.append("checkbox_64.png");
-		else if (density > 2.0f)
-			sprite_path.append("checkbox_32.png");
-		else
-			sprite_path.append("checkbox_16.png");
-		// Texture dimensions should be a power of 2
-		gui::IGUISpriteBank *sprites = skin->getSpriteBank();
-		video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
-		video::ITexture *sprite_texture = driver->getTexture(sprite_path.c_str());
-		if (sprite_texture) {
-			s32 sprite_id = sprites->addTextureAsSprite(sprite_texture);
-			if (sprite_id != -1)
-				skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, sprite_id);
-		}
-	}
+	init_guienv(guienv);
 
 	g_fontengine = new FontEngine(guienv);
-	FATAL_ERROR_IF(g_fontengine == NULL, "Font engine creation failed.");
-
-	// Irrlicht 1.8 input colours
-	skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
-	skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
+	FATAL_ERROR_IF(!g_fontengine, "Font engine creation failed.");
 
 	// Create the menu clouds
-	if (!g_menucloudsmgr)
-		g_menucloudsmgr = m_rendering_engine->get_scene_manager()->createNewSceneManager();
-	if (!g_menuclouds)
-		g_menuclouds = new Clouds(g_menucloudsmgr, -1, rand());
+	// This is only global so it can be used by RenderingEngine::draw_load_screen().
+	assert(!g_menucloudsmgr && !g_menuclouds);
+	g_menucloudsmgr = m_rendering_engine->get_scene_manager()->createNewSceneManager();
+	g_menuclouds = new Clouds(g_menucloudsmgr, nullptr, -1, rand());
 	g_menuclouds->setHeight(100.0f);
 	g_menuclouds->update(v3f(0, 0, 0), video::SColor(255, 240, 240, 255));
 	scene::ICameraSceneNode* camera;
@@ -223,7 +188,7 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 			guiroot = m_rendering_engine->get_gui_env()->addStaticText(L"",
 				core::rect<s32>(0, 0, 10000, 10000));
 
-			bool game_has_run = launch_game(error_message, reconnect_requested,
+			bool should_run_game = launch_game(error_message, reconnect_requested,
 				start_data, cmd_args);
 
 			// Reset the reconnect_requested flag
@@ -232,13 +197,11 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 			// If skip_main_menu, we only want to startup once
 			if (skip_main_menu && !first_loop)
 				break;
-
 			first_loop = false;
 
-			if (!game_has_run) {
+			if (!should_run_game) {
 				if (skip_main_menu)
 					break;
-
 				continue;
 			}
 
@@ -246,9 +209,6 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 			if (!m_rendering_engine->run() || *kill)
 				break;
 
-			m_rendering_engine->get_video_driver()->setTextureCreationFlag(
-					video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
-
 			if (g_settings->getBool("enable_touch")) {
 				receiver->m_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver);
 				g_touchscreengui = receiver->m_touchscreengui;
@@ -301,17 +261,18 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 
 		// If no main menu, show error and exit
 		if (skip_main_menu) {
-			if (!error_message.empty()) {
-				verbosestream << "error_message = "
-				              << error_message << std::endl;
+			if (!error_message.empty())
 				retval = false;
-			}
 			break;
 		}
 	} // Menu-game loop
 
+	assert(g_menuclouds->getReferenceCount() == 1);
 	g_menuclouds->drop();
+	g_menuclouds = nullptr;
+	assert(g_menucloudsmgr->getReferenceCount() == 1);
 	g_menucloudsmgr->drop();
+	g_menucloudsmgr = nullptr;
 
 	return retval;
 }
@@ -373,6 +334,45 @@ void ClientLauncher::init_input()
 	}
 }
 
+void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv)
+{
+	gui::IGUISkin *skin = guienv->getSkin();
+
+	skin->setColor(gui::EGDC_WINDOW_SYMBOL, video::SColor(255, 255, 255, 255));
+	skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
+	skin->setColor(gui::EGDC_3D_LIGHT, video::SColor(0, 0, 0, 0));
+	skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 30, 30, 30));
+	skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
+	skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 120, 50));
+	skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
+	skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
+	skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
+
+	float density = rangelim(g_settings->getFloat("gui_scaling"), 0.5f, 20) *
+		RenderingEngine::getDisplayDensity();
+	skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density));
+	skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(14.0f * density));
+	skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density));
+	if (density > 1.5f) {
+		std::string sprite_path = porting::path_share + "/textures/base/pack/";
+		if (density > 3.5f)
+			sprite_path.append("checkbox_64.png");
+		else if (density > 2.0f)
+			sprite_path.append("checkbox_32.png");
+		else
+			sprite_path.append("checkbox_16.png");
+		// Texture dimensions should be a power of 2
+		gui::IGUISpriteBank *sprites = skin->getSpriteBank();
+		video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
+		video::ITexture *sprite_texture = driver->getTexture(sprite_path.c_str());
+		if (sprite_texture) {
+			s32 sprite_id = sprites->addTextureAsSprite(sprite_texture);
+			if (sprite_id != -1)
+				skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, sprite_id);
+		}
+	}
+}
+
 bool ClientLauncher::launch_game(std::string &error_message,
 		bool reconnect_requested, GameStartData &start_data,
 		const Settings &cmd_args)

+ 1 - 1
src/client/clientlauncher.h

@@ -38,6 +38,7 @@ private:
 	void init_args(GameStartData &start_data, const Settings &cmd_args);
 	bool init_engine();
 	void init_input();
+	void init_guienv(gui::IGUIEnvironment *guienv);
 
 	bool launch_game(std::string &error_message, bool reconnect_requested,
 		GameStartData &start_data, const Settings &cmd_args);
@@ -49,5 +50,4 @@ private:
 	RenderingEngine *m_rendering_engine = nullptr;
 	InputHandler *input = nullptr;
 	MyEventReceiver *receiver = nullptr;
-	gui::IGUISkin *skin = nullptr;
 };

+ 154 - 90
src/client/clouds.cpp

@@ -18,10 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "client/renderingengine.h"
+#include "client/shader.h"
 #include "clouds.h"
-#include "noise.h"
 #include "constants.h"
 #include "debug.h"
+#include "irrlicht_changes/printing.h"
+#include "noise.h"
 #include "profiler.h"
 #include "settings.h"
 #include <cmath>
@@ -40,35 +42,38 @@ static void cloud_3d_setting_changed(const std::string &settingname, void *data)
 	((Clouds *)data)->readSettings();
 }
 
-Clouds::Clouds(scene::ISceneManager* mgr,
+Clouds::Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc,
 		s32 id,
 		u32 seed
 ):
 	scene::ISceneNode(mgr->getRootSceneNode(), mgr, id),
 	m_seed(seed)
 {
+	m_enable_shaders = g_settings->getBool("enable_shaders");
+	// menu clouds use shader-less clouds for simplicity (ssrc == NULL)
+	m_enable_shaders = m_enable_shaders && ssrc;
+
 	m_material.Lighting = false;
 	m_material.BackfaceCulling = true;
 	m_material.FogEnable = true;
 	m_material.AntiAliasing = video::EAAM_SIMPLE;
-	m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-	m_material.forEachTexture([] (auto &tex) {
-		tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
-		tex.MagFilter = video::ETMAGF_NEAREST;
-	});
-
-	m_params.height        = 120;
-	m_params.density       = 0.4f;
-	m_params.thickness     = 16.0f;
-	m_params.color_bright  = video::SColor(229, 240, 240, 255);
-	m_params.color_ambient = video::SColor(255, 0, 0, 0);
-	m_params.speed         = v2f(0.0f, -2.0f);
+	if (m_enable_shaders) {
+		auto sid = ssrc->getShader("cloud_shader", TILE_MATERIAL_ALPHA);
+		m_material.MaterialType = ssrc->getShaderInfo(sid).material;
+	} else {
+		m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+	}
+
+	m_params = SkyboxDefaults::getCloudDefaults();
 
 	readSettings();
 	g_settings->registerChangedCallback("enable_3d_clouds",
 		&cloud_3d_setting_changed, this);
 
 	updateBox();
+
+	m_meshbuffer.reset(new scene::SMeshBuffer());
+	m_meshbuffer->setHardwareMappingHint(scene::EHM_DYNAMIC);
 }
 
 Clouds::~Clouds()
@@ -82,38 +87,14 @@ void Clouds::OnRegisterSceneNode()
 	if(IsVisible)
 	{
 		SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
-		//SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
 	}
 
 	ISceneNode::OnRegisterSceneNode();
 }
 
-void Clouds::render()
+void Clouds::updateMesh()
 {
-
-	if (m_params.density <= 0.0f)
-		return; // no need to do anything
-
-	video::IVideoDriver* driver = SceneManager->getVideoDriver();
-
-	if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT)
-	//if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SOLID)
-		return;
-
-	ScopeProfiler sp(g_profiler, "Clouds::render()", SPT_AVG);
-
-	int num_faces_to_draw = m_enable_3d ? 6 : 1;
-
-	m_material.BackfaceCulling = m_enable_3d;
-
-	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
-	driver->setMaterial(m_material);
-
-	/*
-		Clouds move from Z+ towards Z-
-	*/
-
-	const float cloud_full_radius = cloud_size * m_cloud_radius_i;
+	// Clouds move from Z+ towards Z-
 
 	v2f camera_pos_2d(m_camera_pos.X, m_camera_pos.Z);
 	// Position of cloud noise origin from the camera
@@ -126,56 +107,61 @@ void Clouds::render()
 		std::floor(center_of_drawing_in_noise_f.Y / cloud_size)
 	);
 
+	// Only update mesh if it has moved enough, this saves lots of GPU buffer uploads.
+	constexpr float max_d = 5 * BS;
+
+	if (!m_mesh_valid) {
+		// mesh was never created or invalidated
+	} else if (m_mesh_origin.getDistanceFrom(m_origin) >= max_d) {
+		// clouds moved
+	} else if (center_of_drawing_in_noise_i != m_last_noise_center) {
+		// noise offset changed
+		// I think in practice this never happens due to the camera offset
+		// being smaller than the cloud size.(?)
+	} else {
+		return;
+	}
+
+	ScopeProfiler sp(g_profiler, "Clouds::updateMesh()", SPT_AVG);
+	m_mesh_origin = m_origin;
+	m_last_noise_center = center_of_drawing_in_noise_i;
+	m_mesh_valid = true;
+
+	const u32 num_faces_to_draw = m_enable_3d ? 6 : 1;
+
 	// The world position of the integer center point of drawing in the noise
 	v2f world_center_of_drawing_in_noise_f = v2f(
 		center_of_drawing_in_noise_i.X * cloud_size,
 		center_of_drawing_in_noise_i.Y * cloud_size
 	) + m_origin;
 
-	/*video::SColor c_top(128,b*240,b*240,b*255);
-	video::SColor c_side_1(128,b*230,b*230,b*255);
-	video::SColor c_side_2(128,b*220,b*220,b*245);
-	video::SColor c_bottom(128,b*205,b*205,b*230);*/
+	// Colors with primitive shading
+
 	video::SColorf c_top_f(m_color);
 	video::SColorf c_side_1_f(m_color);
 	video::SColorf c_side_2_f(m_color);
 	video::SColorf c_bottom_f(m_color);
-	c_side_1_f.r *= 0.95;
-	c_side_1_f.g *= 0.95;
-	c_side_1_f.b *= 0.95;
-	c_side_2_f.r *= 0.90;
-	c_side_2_f.g *= 0.90;
-	c_side_2_f.b *= 0.90;
-	c_bottom_f.r *= 0.80;
-	c_bottom_f.g *= 0.80;
-	c_bottom_f.b *= 0.80;
+	if (m_enable_shaders) {
+		// shader mixes the base color, set via EmissiveColor
+		c_top_f = c_side_1_f = c_side_2_f = c_bottom_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
+	}
+	c_side_1_f.r *= 0.95f;
+	c_side_1_f.g *= 0.95f;
+	c_side_1_f.b *= 0.95f;
+	c_side_2_f.r *= 0.90f;
+	c_side_2_f.g *= 0.90f;
+	c_side_2_f.b *= 0.90f;
+	c_bottom_f.r *= 0.80f;
+	c_bottom_f.g *= 0.80f;
+	c_bottom_f.b *= 0.80f;
 	video::SColor c_top = c_top_f.toSColor();
 	video::SColor c_side_1 = c_side_1_f.toSColor();
 	video::SColor c_side_2 = c_side_2_f.toSColor();
 	video::SColor c_bottom = c_bottom_f.toSColor();
 
-	// Get fog parameters for setting them back later
-	video::SColor fog_color(0,0,0,0);
-	video::E_FOG_TYPE fog_type = video::EFT_FOG_LINEAR;
-	f32 fog_start = 0;
-	f32 fog_end = 0;
-	f32 fog_density = 0;
-	bool fog_pixelfog = false;
-	bool fog_rangefog = false;
-	driver->getFog(fog_color, fog_type, fog_start, fog_end, fog_density,
-			fog_pixelfog, fog_rangefog);
-
-	// Set our own fog, unless it was already disabled
-	if (fog_start < FOG_RANGE_ALL) {
-		driver->setFog(fog_color, fog_type, cloud_full_radius * 0.5,
-			cloud_full_radius*1.2, fog_density, fog_pixelfog, fog_rangefog);
-	}
-
 	// Read noise
 
 	std::vector<bool> grid(m_cloud_radius_i * 2 * m_cloud_radius_i * 2);
-	std::vector<video::S3DVertex> vertices;
-	vertices.reserve(16 * m_cloud_radius_i * m_cloud_radius_i);
 
 	for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) {
 		u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i;
@@ -190,10 +176,23 @@ void Clouds::render()
 		}
 	}
 
+
+	auto *mb = m_meshbuffer.get();
+	{
+		const u32 vertex_count = num_faces_to_draw * 16 * m_cloud_radius_i * m_cloud_radius_i;
+		const u32 quad_count = vertex_count / 4;
+		const u32 index_count = quad_count * 6;
+
+		// reserve memory
+		mb->Vertices.reallocate(vertex_count);
+		mb->Indices.reallocate(index_count);
+	}
+
 #define GETINDEX(x, z, radius) (((z)+(radius))*(radius)*2 + (x)+(radius))
 #define INAREA(x, z, radius) \
 	((x) >= -(radius) && (x) < (radius) && (z) >= -(radius) && (z) < (radius))
 
+	mb->Vertices.set_used(0);
 	for (s16 zi0= -m_cloud_radius_i; zi0 < m_cloud_radius_i; zi0++)
 	for (s16 xi0= -m_cloud_radius_i; xi0 < m_cloud_radius_i; xi0++)
 	{
@@ -224,7 +223,7 @@ void Clouds::render()
 		const f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f;
 		const f32 rz = cloud_size / 2;
 
-		for(int i=0; i<num_faces_to_draw; i++)
+		for(u32 i = 0; i < num_faces_to_draw; i++)
 		{
 			switch(i)
 			{
@@ -310,27 +309,87 @@ void Clouds::render()
 			}
 
 			v3f pos(p0.X, m_params.height * BS, p0.Y);
-			pos -= intToFloat(m_camera_offset, BS);
 
 			for (video::S3DVertex &vertex : v) {
 				vertex.Pos += pos;
-				vertices.push_back(vertex);
+				mb->Vertices.push_back(vertex);
 			}
 		}
 	}
-	int quad_count = vertices.size() / 4;
-	std::vector<u16> indices;
-	indices.reserve(quad_count * 6);
-	for (int k = 0; k < quad_count; k++) {
-		indices.push_back(4 * k + 0);
-		indices.push_back(4 * k + 1);
-		indices.push_back(4 * k + 2);
-		indices.push_back(4 * k + 2);
-		indices.push_back(4 * k + 3);
-		indices.push_back(4 * k + 0);
+	mb->setDirty(scene::EBT_VERTEX);
+
+	const u32 quad_count = mb->getVertexCount() / 4;
+	const u32 index_count = quad_count * 6;
+	// rewrite index array as needed
+	if (mb->getIndexCount() > index_count) {
+		mb->Indices.set_used(index_count);
+		mb->setDirty(scene::EBT_INDEX);
+	} else if (mb->getIndexCount() < index_count) {
+		const u32 start = mb->getIndexCount() / 6;
+		assert(start * 6 == mb->getIndexCount());
+		for (u32 k = start; k < quad_count; k++) {
+			mb->Indices.push_back(4 * k + 0);
+			mb->Indices.push_back(4 * k + 1);
+			mb->Indices.push_back(4 * k + 2);
+			mb->Indices.push_back(4 * k + 2);
+			mb->Indices.push_back(4 * k + 3);
+			mb->Indices.push_back(4 * k + 0);
+		}
+		mb->setDirty(scene::EBT_INDEX);
+	}
+
+	tracestream << "Cloud::updateMesh(): " << mb->getVertexCount() << " vertices"
+		<< std::endl;
+}
+
+void Clouds::render()
+{
+	if (m_params.density <= 0.0f)
+		return; // no need to do anything
+
+	video::IVideoDriver* driver = SceneManager->getVideoDriver();
+
+	if (SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT)
+		return;
+
+	updateMesh();
+
+	// Update position
+	{
+		v2f off_origin = m_origin - m_mesh_origin;
+		v3f rel(off_origin.X, 0, off_origin.Y);
+		rel -= intToFloat(m_camera_offset, BS);
+		setPosition(rel);
+		updateAbsolutePosition();
 	}
-	driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count,
-			video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
+
+	m_material.BackfaceCulling = m_enable_3d;
+	if (m_enable_shaders)
+		m_material.EmissiveColor = m_color.toSColor();
+
+	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
+	driver->setMaterial(m_material);
+
+	const float cloud_full_radius = cloud_size * m_cloud_radius_i;
+
+	// Get fog parameters for setting them back later
+	video::SColor fog_color(0,0,0,0);
+	video::E_FOG_TYPE fog_type = video::EFT_FOG_LINEAR;
+	f32 fog_start = 0;
+	f32 fog_end = 0;
+	f32 fog_density = 0;
+	bool fog_pixelfog = false;
+	bool fog_rangefog = false;
+	driver->getFog(fog_color, fog_type, fog_start, fog_end, fog_density,
+			fog_pixelfog, fog_rangefog);
+
+	// Set our own fog, unless it was already disabled
+	if (fog_start < FOG_RANGE_ALL) {
+		driver->setFog(fog_color, fog_type, cloud_full_radius * 0.5,
+				cloud_full_radius*1.2, fog_density, fog_pixelfog, fog_rangefog);
+	}
+
+	driver->drawMeshBuffer(m_meshbuffer.get());
 
 	// Restore fog settings
 	driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density,
@@ -346,13 +405,13 @@ void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse)
 {
 	video::SColorf ambient(m_params.color_ambient);
 	video::SColorf bright(m_params.color_bright);
-	m_camera_pos = camera_p;
 	m_color.r = core::clamp(color_diffuse.r * bright.r, ambient.r, 1.0f);
 	m_color.g = core::clamp(color_diffuse.g * bright.g, ambient.g, 1.0f);
 	m_color.b = core::clamp(color_diffuse.b * bright.b, ambient.b, 1.0f);
 	m_color.a = bright.a;
 
 	// is the camera inside the cloud mesh?
+	m_camera_pos = camera_p;
 	m_camera_inside_cloud = false; // default
 	if (m_enable_3d) {
 		float camera_height = camera_p.Y - BS * m_camera_offset.Y;
@@ -369,9 +428,14 @@ void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse)
 
 void Clouds::readSettings()
 {
-	// Upper limit was chosen due to posible render bugs
-	m_cloud_radius_i = rangelim(g_settings->getU16("cloud_radius"), 1, 62);
+	// The code isn't designed to go over 64k vertices so the upper limits were
+	// chosen to avoid exactly that.
+	// refer to vertex_count in updateMesh()
 	m_enable_3d = g_settings->getBool("enable_3d_clouds");
+	const u16 maximum = m_enable_3d ? 62 : 25;
+	m_cloud_radius_i = rangelim(g_settings->getU16("cloud_radius"), 1, maximum);
+
+	invalidateMesh();
 }
 
 bool Clouds::gridFilled(int x, int y) const

+ 38 - 12
src/client/clouds.h

@@ -19,10 +19,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "irrlichttypes_extrabloated.h"
-#include <iostream>
 #include "constants.h"
+#include "irr_ptr.h"
+#include "irrlichttypes_extrabloated.h"
 #include "skyparams.h"
+#include <iostream>
+
+class IShaderSource;
 
 // Menu clouds
 class Clouds;
@@ -34,7 +37,7 @@ extern scene::ISceneManager *g_menucloudsmgr;
 class Clouds : public scene::ISceneNode
 {
 public:
-	Clouds(scene::ISceneManager* mgr,
+	Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc,
 			s32 id,
 			u32 seed
 	);
@@ -72,7 +75,7 @@ public:
 
 	void update(const v3f &camera_p, const video::SColorf &color);
 
-	void updateCameraOffset(const v3s16 &camera_offset)
+	void updateCameraOffset(v3s16 camera_offset)
 	{
 		m_camera_offset = camera_offset;
 		updateBox();
@@ -82,24 +85,29 @@ public:
 
 	void setDensity(float density)
 	{
+		if (m_params.density == density)
+			return;
 		m_params.density = density;
-		// currently does not need bounding
+		invalidateMesh();
 	}
 
-	void setColorBright(const video::SColor &color_bright)
+	void setColorBright(video::SColor color_bright)
 	{
 		m_params.color_bright = color_bright;
 	}
 
-	void setColorAmbient(const video::SColor &color_ambient)
+	void setColorAmbient(video::SColor color_ambient)
 	{
 		m_params.color_ambient = color_ambient;
 	}
 
 	void setHeight(float height)
 	{
-		m_params.height = height; // add bounding when necessary
+		if (m_params.height == height)
+			return;
+		m_params.height = height;
 		updateBox();
+		invalidateMesh();
 	}
 
 	void setSpeed(v2f speed)
@@ -109,8 +117,11 @@ public:
 
 	void setThickness(float thickness)
 	{
+		if (m_params.thickness == thickness)
+			return;
 		m_params.thickness = thickness;
 		updateBox();
+		invalidateMesh();
 	}
 
 	bool isCameraInsideCloud() const { return m_camera_inside_cloud; }
@@ -126,18 +137,33 @@ private:
 				BS * 1000000.0f, height_bs + thickness_bs - BS * m_camera_offset.Y, BS * 1000000.0f);
 	}
 
+	void updateMesh();
+	void invalidateMesh()
+	{
+		m_mesh_valid = false;
+	}
+
 	bool gridFilled(int x, int y) const;
 
 	video::SMaterial m_material;
+	irr_ptr<scene::SMeshBuffer> m_meshbuffer;
+	// Value of m_origin at the time the mesh was last updated
+	v2f m_mesh_origin;
+	// Value of center_of_drawing_in_noise_i at the time the mesh was last updated
+	v2s16 m_last_noise_center;
+	// Was the mesh ever generated?
+	bool m_mesh_valid = false;
+
 	aabb3f m_box;
+	v2f m_origin;
 	u16 m_cloud_radius_i;
-	bool m_enable_3d;
 	u32 m_seed;
 	v3f m_camera_pos;
-	v2f m_origin;
+
 	v3s16 m_camera_offset;
-	video::SColorf m_color = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
-	CloudParams m_params;
 	bool m_camera_inside_cloud = false;
 
+	bool m_enable_shaders, m_enable_3d;
+	video::SColorf m_color = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
+	CloudParams m_params;
 };

+ 0 - 1
src/client/content_cao.cpp

@@ -812,7 +812,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
 			(m_prop.visual == "wielditem"));
 
 		m_wield_meshnode->setScale(m_prop.visual_size / 2.0f);
-		m_wield_meshnode->setColor(video::SColor(0xFFFFFFFF));
 	} else {
 		infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
 				<<"\" not supported"<<std::endl;

+ 12 - 12
src/client/game.cpp

@@ -379,7 +379,6 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
 	CachedPixelShaderSetting<float>
 		m_animation_timer_delta_pixel{"animationTimerDelta"};
 	CachedPixelShaderSetting<float, 3> m_day_light{"dayLight"};
-	CachedPixelShaderSetting<float, 4> m_star_color{"starColor"};
 	CachedPixelShaderSetting<float, 3> m_eye_position_pixel{"eyePosition"};
 	CachedVertexShaderSetting<float, 3> m_eye_position_vertex{"eyePosition"};
 	CachedPixelShaderSetting<float, 3> m_minimap_yaw{"yawVec"};
@@ -473,10 +472,6 @@ public:
 		get_sunlight_color(&sunlight, daynight_ratio);
 		m_day_light.set(sunlight, services);
 
-		video::SColorf star_color = m_sky->getCurrentStarColor();
-		float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
-		m_star_color.set(clr, services);
-
 		u32 animation_timer = m_client->getEnv().getFrameTime() % 1000000;
 		float animation_timer_f = (float)animation_timer / 100000.f;
 		m_animation_timer_vertex.set(&animation_timer_f, services);
@@ -529,7 +524,9 @@ public:
 			m_bloom_radius_pixel.set(&m_bloom_radius, services);
 			m_bloom_strength_pixel.set(&m_bloom_strength, services);
 		}
-		float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation;
+
+		const auto &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
+		float saturation = lighting.saturation;
 		m_saturation_pixel.set(&saturation, services);
 
 		if (m_volumetric_light_enabled) {
@@ -540,13 +537,13 @@ public:
 
 			if (m_sky->getSunVisible()) {
 				v3f sun_position = camera_node->getAbsolutePosition() +
-						10000. * m_sky->getSunDirection();
+						10000.f * m_sky->getSunDirection();
 				transform.transformVect(sun_position);
 				sun_position.normalize();
 
 				m_sun_position_pixel.set(sun_position, services);
 
-				float sun_brightness = rangelim(107.143f * m_sky->getSunDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f);
+				float sun_brightness = core::clamp(107.143f * m_sky->getSunDirection().Y, 0.f, 1.f);
 				m_sun_brightness_pixel.set(&sun_brightness, services);
 			} else {
 				m_sun_position_pixel.set(v3f(0.f, 0.f, -1.f), services);
@@ -557,13 +554,13 @@ public:
 
 			if (m_sky->getMoonVisible()) {
 				v3f moon_position = camera_node->getAbsolutePosition() +
-						10000. * m_sky->getMoonDirection();
+						10000.f * m_sky->getMoonDirection();
 				transform.transformVect(moon_position);
 				moon_position.normalize();
 
 				m_moon_position_pixel.set(moon_position, services);
 
-				float moon_brightness = rangelim(107.143f * m_sky->getMoonDirection().dotProduct(v3f(0.f, 1.f, 0.f)), 0.f, 1.f);
+				float moon_brightness = core::clamp(107.143f * m_sky->getMoonDirection().Y, 0.f, 1.f);
 				m_moon_brightness_pixel.set(&moon_brightness, services);
 			} else {
 				m_moon_position_pixel.set(v3f(0.f, 0.f, -1.f), services);
@@ -571,7 +568,8 @@ public:
 				float moon_brightness = 0.f;
 				m_moon_brightness_pixel.set(&moon_brightness, services);
 			}
-			float volumetric_light_strength = m_client->getEnv().getLocalPlayer()->getLighting().volumetric_light_strength;
+
+			float volumetric_light_strength = lighting.volumetric_light_strength;
 			m_volumetric_light_strength_pixel.set(&volumetric_light_strength, services);
 		}
 	}
@@ -1099,6 +1097,8 @@ bool Game::startup(bool *kill,
 	driver = device->getVideoDriver();
 	smgr = m_rendering_engine->get_scene_manager();
 
+	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
+
 	smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
 
 	// Reinit runData
@@ -1454,7 +1454,7 @@ bool Game::createClient(const GameStartData &start_data)
 	/* Clouds
 	 */
 	if (m_cache_enable_clouds)
-		clouds = new Clouds(smgr, -1, time(0));
+		clouds = new Clouds(smgr, shader_src, -1, rand());
 
 	/* Skybox
 	 */

+ 13 - 14
src/client/hud.cpp

@@ -97,7 +97,7 @@ Hud::Hud(Client *client, LocalPlayer *player,
 
 	if (g_settings->getBool("enable_shaders")) {
 		IShaderSource *shdrsrc = client->getShaderSource();
-		u16 shader_id = shdrsrc->getShader(
+		auto shader_id = shdrsrc->getShader(
 			m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", TILE_MATERIAL_ALPHA);
 		m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
 	} else {
@@ -1100,24 +1100,23 @@ void drawItemStack(
 		video::SColor basecolor =
 			client->idef()->getItemstackColor(item, client);
 
-		u32 mc = mesh->getMeshBufferCount();
+		const u32 mc = mesh->getMeshBufferCount();
+		if (mc > imesh->buffer_colors.size())
+			imesh->buffer_colors.resize(mc);
 		for (u32 j = 0; j < mc; ++j) {
 			scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
-			// we can modify vertices relatively fast,
-			// because these meshes are not buffered.
-			assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
 			video::SColor c = basecolor;
 
-			if (imesh->buffer_colors.size() > j) {
-				ItemPartColor *p = &imesh->buffer_colors[j];
-				if (p->override_base)
-					c = p->color;
-			}
+			auto &p = imesh->buffer_colors[j];
+			p.applyOverride(c);
 
-			if (imesh->needs_shading)
-				colorizeMeshBuffer(buf, &c);
-			else
-				setMeshBufferColor(buf, c);
+			if (p.needColorize(c)) {
+				buf->setDirty(scene::EBT_VERTEX);
+				if (imesh->needs_shading)
+					colorizeMeshBuffer(buf, &c);
+				else
+					setMeshBufferColor(buf, c);
+			}
 
 			video::SMaterial &material = buf->getMaterial();
 			material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;

+ 1 - 1
src/client/minimap.cpp

@@ -615,7 +615,7 @@ void Minimap::drawMinimap(core::rect<s32> rect)
 	material.TextureLayers[1].Texture = data->heightmap_texture;
 
 	if (m_enable_shaders && data->mode.type == MINIMAP_TYPE_SURFACE) {
-		u16 sid = m_shdrsrc->getShader("minimap_shader", TILE_MATERIAL_ALPHA);
+		auto sid = m_shdrsrc->getShader("minimap_shader", TILE_MATERIAL_ALPHA);
 		material.MaterialType = m_shdrsrc->getShaderInfo(sid).material;
 	} else {
 		material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;

+ 13 - 11
src/client/renderingengine.cpp

@@ -94,7 +94,7 @@ class FogShaderConstantSetter : public IShaderConstantSetter
 public:
 	void onSetConstants(video::IMaterialRendererServices *services) override
 	{
-		auto *driver = RenderingEngine::get_video_driver();
+		auto *driver = services->getVideoDriver();
 		assert(driver);
 
 		video::SColor fog_color(0);
@@ -314,15 +314,17 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
 			gui::StaticText::add(guienv, text, textrect, false, false);
 	guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
 
-	if (sky && g_settings->getBool("menu_clouds")) {
-		g_menuclouds->step(dtime * 3);
-		g_menuclouds->render();
-		get_video_driver()->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
-		g_menucloudsmgr->drawAll();
-	} else if (sky)
-		get_video_driver()->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
-	else
-		get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
+	auto *driver = get_video_driver();
+
+	if (sky) {
+		driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
+		if (g_settings->getBool("menu_clouds")) {
+			g_menuclouds->step(dtime * 3);
+			g_menucloudsmgr->drawAll();
+		}
+	} else {
+		driver->beginScene(true, true, video::SColor(255, 0, 0, 0));
+	}
 
 	// draw progress bar
 	if ((percent >= 0) && (percent <= 100)) {
@@ -367,7 +369,7 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
 	}
 
 	guienv->drawAll();
-	get_video_driver()->endScene();
+	driver->endScene();
 	guitext->remove();
 }
 

+ 0 - 1
src/client/renderingengine.h

@@ -90,7 +90,6 @@ public:
 
 	bool setupTopLevelWindow();
 	bool setWindowIcon();
-	static bool print_video_modes();
 	void cleanupMeshCache();
 
 	void removeMesh(const scene::IMesh* mesh);

+ 15 - 11
src/client/shader.cpp

@@ -217,13 +217,22 @@ class MainShaderConstantSetter : public IShaderConstantSetter
 	// Texture matrix
 	CachedVertexShaderSetting<float, 16> m_texture{"mTexture"};
 
+	// commonly used way to pass material color to shader
+	video::SColor m_emissive_color;
+	CachedPixelShaderSetting<float, 4> m_emissive_color_setting{"emissiveColor"};
+
 public:
 	~MainShaderConstantSetter() = default;
 
+	virtual void onSetMaterial(const video::SMaterial& material) override
+	{
+		m_emissive_color = material.EmissiveColor;
+	}
+
 	virtual void onSetConstants(video::IMaterialRendererServices *services) override
 	{
 		video::IVideoDriver *driver = services->getVideoDriver();
-		sanity_check(driver);
+		assert(driver);
 
 		// Set world matrix
 		core::matrix4 world = driver->getTransform(video::ETS_WORLD);
@@ -244,6 +253,9 @@ public:
 			m_world_view.set(worldView, services);
 			m_texture.set(texture, services);
 		}
+
+		video::SColorf emissive_color(m_emissive_color);
+		m_emissive_color_setting.set(emissive_color, services);
 	}
 };
 
@@ -545,11 +557,11 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
 		return shaderinfo;
 
 	video::IVideoDriver *driver = RenderingEngine::get_video_driver();
-	if (!driver->queryFeature(video::EVDF_ARB_GLSL)) {
+	video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
+	if (!driver->queryFeature(video::EVDF_ARB_GLSL) || !gpu) {
 		throw ShaderException(gettext("Shaders are enabled but GLSL is not "
 			"supported by the driver."));
 	}
-	video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
 
 	// Create shaders header
 	bool fully_programmable = driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3;
@@ -610,14 +622,6 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
 		#define textureFlags texture2
 	)";
 
-	// Since this is the first time we're using the GL bindings be extra careful.
-	// This should be removed before 5.6.0 or similar.
-	if (!GL.GetString) {
-		errorstream << "OpenGL procedures were not loaded correctly, "
-			"please open a bug report with details about your platform/OS." << std::endl;
-		abort();
-	}
-
 	bool use_discard = fully_programmable;
 	// For renderers that should use discard instead of GL_ALPHA_TEST
 	const char *renderer = reinterpret_cast<const char*>(GL.GetString(GL.RENDERER));

+ 6 - 4
src/client/sky.cpp

@@ -77,6 +77,7 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade
 	// Create materials
 
 	m_materials[0] = baseMaterial();
+	// FIXME: shouldn't this check m_enable_shaders?
 	m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material;
 	m_materials[0].Lighting = true;
 	m_materials[0].ColorMaterial = video::ECM_NONE;
@@ -683,11 +684,12 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
 	float starbrightness = (0.25f - std::abs(tod)) * 20.0f;
 	float alpha = clamp(starbrightness, day_opacity, 1.0f);
 
-	m_star_color = m_star_params.starcolor;
-	m_star_color.a *= alpha;
-	if (m_star_color.a <= 0.0f) // Stars are only drawn when not fully transparent
+	video::SColorf color(m_star_params.starcolor);
+	color.a *= alpha;
+	if (color.a <= 0.0f) // Stars are only drawn when not fully transparent
 		return;
-	m_materials[0].DiffuseColor = m_materials[0].EmissiveColor = m_star_color.toSColor();
+	m_materials[0].EmissiveColor = color.toSColor();
+
 	auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f));
 	auto world_matrix = driver->getTransform(video::ETS_WORLD);
 	driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation);

+ 0 - 2
src/client/sky.h

@@ -114,7 +114,6 @@ public:
 	void clearSkyboxTextures() { m_sky_params.textures.clear(); }
 	void addTextureToSkybox(const std::string &texture, int material_id,
 		ITextureSource *tsrc);
-	const video::SColorf &getCurrentStarColor() const { return m_star_color; }
 
 	// Note: the Sky class doesn't use these values. It just stores them.
 	void setFogDistance(s16 fog_distance) { m_sky_params.fog_distance = fog_distance; }
@@ -210,7 +209,6 @@ private:
 
 	u64 m_seed = 0;
 	irr_ptr<scene::SMeshBuffer> m_stars;
-	video::SColorf m_star_color;
 
 	video::ITexture *m_sun_texture;
 	video::ITexture *m_moon_texture;

+ 31 - 16
src/client/wieldmesh.cpp

@@ -508,21 +508,26 @@ void WieldMeshSceneNode::setColor(video::SColor c)
 	u8 red = c.getRed();
 	u8 green = c.getGreen();
 	u8 blue = c.getBlue();
-	u32 mc = mesh->getMeshBufferCount();
+
+	const u32 mc = mesh->getMeshBufferCount();
+	if (mc > m_colors.size())
+		m_colors.resize(mc);
 	for (u32 j = 0; j < mc; j++) {
 		video::SColor bc(m_base_color);
-		if ((m_colors.size() > j) && (m_colors[j].override_base))
-			bc = m_colors[j].color;
+		m_colors[j].applyOverride(bc);
 		video::SColor buffercolor(255,
 			bc.getRed() * red / 255,
 			bc.getGreen() * green / 255,
 			bc.getBlue() * blue / 255);
 		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
 
-		if (m_enable_shaders)
-			setMeshBufferColor(buf, buffercolor);
-		else
-			colorizeMeshBuffer(buf, &buffercolor);
+		if (m_colors[j].needColorize(buffercolor)) {
+			buf->setDirty(scene::EBT_VERTEX);
+			if (m_enable_shaders)
+				setMeshBufferColor(buf, buffercolor);
+			else
+				colorizeMeshBuffer(buf, &buffercolor);
+		}
 	}
 }
 
@@ -536,8 +541,7 @@ void WieldMeshSceneNode::setNodeLightColor(video::SColor color)
 			video::SMaterial &material = m_meshnode->getMaterial(i);
 			material.EmissiveColor = color;
 		}
-	}
-	else {
+	} else {
 		setColor(color);
 	}
 }
@@ -557,6 +561,12 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
 		dummymesh->drop();  // m_meshnode grabbed it
 	} else {
 		m_meshnode->setMesh(mesh);
+		// without shaders recolored often for lighting
+		// otherwise only once
+		if (m_enable_shaders)
+			mesh->setHardwareMappingHint(scene::EHM_STATIC);
+		else
+			mesh->setHardwareMappingHint(scene::EHM_DYNAMIC);
 	}
 
 	m_meshnode->forEachMaterial([this] (auto &mat) {
@@ -651,8 +661,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
 		}
 		}
 
-		u32 mc = mesh->getMeshBufferCount();
-		for (u32 i = 0; i < mc; ++i) {
+		for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
 			scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
 			video::SMaterial &material = buf->getMaterial();
 			material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
@@ -668,6 +677,12 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
 		rotateMeshXZby(mesh, -45);
 		rotateMeshYZby(mesh, -30);
 	}
+
+	// might need to be re-colorized, this is done only when needed
+	if (mesh) {
+		mesh->setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_VERTEX);
+		mesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_INDEX);
+	}
 	result->mesh = mesh;
 }
 
@@ -722,11 +737,10 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
 	bool use_shaders, bool set_material, const video::E_MATERIAL_TYPE *mattype,
 	std::vector<ItemPartColor> *colors, bool apply_scale)
 {
-	u32 mc = mesh->getMeshBufferCount();
+	const u32 mc = mesh->getMeshBufferCount();
 	// Allocate colors for existing buffers
 	colors->clear();
-	for (u32 i = 0; i < mc; ++i)
-		colors->push_back(ItemPartColor());
+	colors->resize(mc);
 
 	for (u32 i = 0; i < mc; ++i) {
 		const TileSpec *tile = &(f.tiles[i]);
@@ -741,11 +755,11 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
 				mesh->addMeshBuffer(copy);
 				copy->drop();
 				buf = copy;
-				colors->push_back(
-					ItemPartColor(layer->has_color, layer->color));
+				colors->emplace_back(layer->has_color, layer->color);
 			} else {
 				(*colors)[i] = ItemPartColor(layer->has_color, layer->color);
 			}
+
 			video::SMaterial &material = buf->getMaterial();
 			if (set_material)
 				layer->applyMaterialOptions(material);
@@ -768,6 +782,7 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
 				}
 				material.setTexture(2, layer->flags_texture);
 			}
+
 			if (apply_scale && tile->world_aligned) {
 				u32 n = buf->getVertexCount();
 				for (u32 k = 0; k != n; ++k)

+ 30 - 14
src/client/wieldmesh.h

@@ -29,38 +29,54 @@ class ITextureSource;
 struct ContentFeatures;
 class ShadowRenderer;
 
-/*!
+/*
  * Holds color information of an item mesh's buffer.
  */
-struct ItemPartColor
+class ItemPartColor
 {
-	/*!
-	 * If this is false, the global base color of the item
-	 * will be used instead of the specific color of the
-	 * buffer.
+	/*
+	 * Optional color that overrides the global base color.
 	 */
-	bool override_base = false;
-	/*!
-	 * The color of the buffer.
+	video::SColor override_color;
+	/*
+	 * Stores the last color this mesh buffer was colorized as.
 	 */
-	video::SColor color = 0;
+	video::SColor last_colorized;
+
+	// saves some bytes compared to two std::optionals
+	bool override_color_set = false;
+	bool last_colorized_set = false;
+
+public:
 
 	ItemPartColor() = default;
 
 	ItemPartColor(bool override, video::SColor color) :
-			override_base(override), color(color)
-	{
+		override_color(color), override_color_set(override)
+	{}
+
+	void applyOverride(video::SColor &dest) const {
+		if (override_color_set)
+			dest = override_color;
+	}
+
+	bool needColorize(video::SColor target) {
+		if (last_colorized_set && target == last_colorized)
+			return false;
+		last_colorized_set = true;
+		last_colorized = target;
+		return true;
 	}
 };
 
 struct ItemMesh
 {
 	scene::IMesh *mesh = nullptr;
-	/*!
+	/*
 	 * Stores the color of each mesh buffer.
 	 */
 	std::vector<ItemPartColor> buffer_colors;
-	/*!
+	/*
 	 * If false, all faces of the item should have the same brightness.
 	 * Disables shading based on normal vectors.
 	 */

+ 28 - 23
src/gui/guiEngine.cpp

@@ -19,25 +19,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "guiEngine.h"
 
-#include <IGUIStaticText.h>
-#include <ICameraSceneNode.h>
+#include "client/fontengine.h"
+#include "client/guiscalingfilter.h"
 #include "client/renderingengine.h"
-#include "scripting_mainmenu.h"
+#include "client/shader.h"
+#include "client/tile.h"
 #include "config.h"
-#include "version.h"
-#include "porting.h"
+#include "content/content.h"
+#include "content/mods.h"
 #include "filesys.h"
-#include "settings.h"
 #include "guiMainMenu.h"
-#include "sound.h"
 #include "httpfetch.h"
-#include "log.h"
-#include "client/fontengine.h"
-#include "client/guiscalingfilter.h"
 #include "irrlicht_changes/static_text.h"
-#include "client/tile.h"
-#include "content/content.h"
-#include "content/mods.h"
+#include "log.h"
+#include "porting.h"
+#include "scripting_mainmenu.h"
+#include "settings.h"
+#include "sound.h"
+#include "version.h"
+#include <ICameraSceneNode.h>
+#include <IGUIStaticText.h>
 
 #if USE_SOUND
 	#include "client/sound/sound_openal.h"
@@ -139,6 +140,10 @@ GUIEngine::GUIEngine(JoystickController *joystick,
 	// create texture source
 	m_texture_source = std::make_unique<MenuTextureSource>(rendering_engine->get_video_driver());
 
+	// create shader source
+	// (currently only used by clouds)
+	m_shader_source.reset(createShaderSource());
+
 	// create soundmanager
 #if USE_SOUND
 	if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) {
@@ -285,10 +290,10 @@ bool GUIEngine::loadMainMenuScript()
 void GUIEngine::run()
 {
 	IrrlichtDevice *device = m_rendering_engine->get_raw_device();
+	video::IVideoDriver *driver = device->getVideoDriver();
+
 	// Always create clouds because they may or may not be
 	// needed based on the game selected
-	video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
-
 	cloudInit();
 
 	unsigned int text_height = g_fontengine->getTextHeight();
@@ -375,23 +380,24 @@ GUIEngine::~GUIEngine()
 
 	m_sound_manager.reset();
 
-	m_irr_toplefttext->setText(L"");
+	m_irr_toplefttext->remove();
+
+	m_cloud.clouds.reset();
 
-	//clean up texture pointers
+	// delete textures
 	for (image_definition &texture : m_textures) {
 		if (texture.texture)
 			m_rendering_engine->get_video_driver()->removeTexture(texture.texture);
 	}
-
-	m_texture_source.reset();
-
-	m_cloud.clouds.reset();
 }
 
 /******************************************************************************/
 void GUIEngine::cloudInit()
 {
-	m_cloud.clouds = make_irr<Clouds>(m_smgr, -1, rand());
+	m_shader_source->addShaderConstantSetterFactory(
+		new FogShaderConstantSetterFactory());
+
+	m_cloud.clouds = make_irr<Clouds>(m_smgr, m_shader_source.get(), -1, rand());
 	m_cloud.clouds->setHeight(100.0f);
 	m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,240,240,255));
 
@@ -404,7 +410,6 @@ void GUIEngine::cloudInit()
 void GUIEngine::drawClouds(float dtime)
 {
 	m_cloud.clouds->step(dtime*3);
-	m_cloud.clouds->render();
 	m_smgr->drawAll();
 }
 

+ 4 - 1
src/gui/guiEngine.h

@@ -54,6 +54,7 @@ struct image_definition {
 class GUIEngine;
 class RenderingEngine;
 class MainMenuScripting;
+class IWritableShaderSource;
 struct MainMenuData;
 
 /******************************************************************************/
@@ -203,7 +204,9 @@ private:
 	MainMenuData                         *m_data = nullptr;
 	/** texture source */
 	std::unique_ptr<ISimpleTextureSource> m_texture_source;
-	/** sound manager*/
+	/** shader source */
+	std::unique_ptr<IWritableShaderSource> m_shader_source;
+	/** sound manager */
 	std::unique_ptr<ISoundManager>        m_sound_manager;
 
 	/** representation of form source to be used in mainmenu formspec */

+ 0 - 4
src/itemdef.cpp

@@ -466,10 +466,6 @@ public:
 		if (!inventory_image.empty())
 			cc->inventory_texture = tsrc->getTexture(inventory_image);
 		getItemMesh(client, item, &(cc->wield_mesh));
-		// note: vertices are modified frequently (see hud.cpp) so only indices
-		// can be mapped
-		if (auto mesh = cc->wield_mesh.mesh)
-			mesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_INDEX);
 
 		cc->palette = tsrc->getPalette(def.palette_image);