1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

VBO-related optimizations and improvements (#14395)

This commit is contained in:
sfan5 2024-02-19 19:04:20 +01:00
parent d85c842ce9
commit bb7f57b095
22 changed files with 439 additions and 322 deletions

View file

@ -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;
});
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.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);
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);
}
driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count,
video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
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();
}
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