|
@@ -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
|