mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 10:03:45 +01:00
Batched rendering of particles (#14489)
Co-authored-by: x2048 <codeforsmile@gmail.com> Co-authored-by: Desour <ds.desour@proton.me>
This commit is contained in:
parent
ff88ed7c75
commit
f8bff346f4
@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "particles.h"
|
#include "particles.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <array>
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "collision.h"
|
#include "collision.h"
|
||||||
#include "client/content_cao.h"
|
#include "client/content_cao.h"
|
||||||
@ -26,21 +27,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "client/renderingengine.h"
|
#include "client/renderingengine.h"
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
#include "light.h"
|
#include "light.h"
|
||||||
|
#include "localplayer.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
#include "clientmap.h"
|
#include "clientmap.h"
|
||||||
#include "mapnode.h"
|
#include "mapnode.h"
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "profiler.h"
|
||||||
|
|
||||||
|
ClientParticleTexture::ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc)
|
||||||
|
{
|
||||||
|
tex = p;
|
||||||
|
// note: getTextureForMesh not needed here because we don't use texture filtering
|
||||||
|
ref = tsrc->getTexture(p.string);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Particle
|
Particle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Particle::Particle(
|
Particle::Particle(
|
||||||
IGameDef *gamedef,
|
|
||||||
LocalPlayer *player,
|
|
||||||
ClientEnvironment *env,
|
|
||||||
const ParticleParameters &p,
|
const ParticleParameters &p,
|
||||||
const ClientParticleTexRef &texture,
|
const ClientParticleTexRef &texture,
|
||||||
v2f texpos,
|
v2f texpos,
|
||||||
@ -49,14 +56,10 @@ Particle::Particle(
|
|||||||
ParticleSpawner *parent,
|
ParticleSpawner *parent,
|
||||||
std::unique_ptr<ClientParticleTexture> owned_texture
|
std::unique_ptr<ClientParticleTexture> owned_texture
|
||||||
) :
|
) :
|
||||||
scene::ISceneNode(((Client *)gamedef)->getSceneManager()->getRootSceneNode(),
|
|
||||||
((Client *)gamedef)->getSceneManager()),
|
|
||||||
|
|
||||||
m_expiration(p.expirationtime),
|
m_expiration(p.expirationtime),
|
||||||
|
|
||||||
m_env(env),
|
m_base_color(color),
|
||||||
m_gamedef(gamedef),
|
|
||||||
m_collisionbox(aabb3f(v3f(-p.size / 2.0f), v3f(p.size / 2.0f))),
|
|
||||||
m_texture(texture),
|
m_texture(texture),
|
||||||
m_texpos(texpos),
|
m_texpos(texpos),
|
||||||
m_texsize(texsize),
|
m_texsize(texsize),
|
||||||
@ -64,102 +67,30 @@ Particle::Particle(
|
|||||||
m_velocity(p.vel),
|
m_velocity(p.vel),
|
||||||
m_acceleration(p.acc),
|
m_acceleration(p.acc),
|
||||||
m_p(p),
|
m_p(p),
|
||||||
m_player(player),
|
|
||||||
|
|
||||||
m_base_color(color),
|
|
||||||
m_color(color),
|
|
||||||
|
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_owned_texture(std::move(owned_texture))
|
m_owned_texture(std::move(owned_texture))
|
||||||
{
|
{
|
||||||
// Set material
|
}
|
||||||
{
|
|
||||||
// translate blend modes to GL blend functions
|
|
||||||
video::E_BLEND_FACTOR bfsrc, bfdst;
|
|
||||||
video::E_BLEND_OPERATION blendop;
|
|
||||||
const auto blendmode = texture.tex != nullptr
|
|
||||||
? texture.tex->blendmode
|
|
||||||
: ParticleParamTypes::BlendMode::alpha;
|
|
||||||
|
|
||||||
switch (blendmode) {
|
Particle::~Particle()
|
||||||
case ParticleParamTypes::BlendMode::add:
|
{
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
if (m_buffer)
|
||||||
bfdst = video::EBF_DST_ALPHA;
|
m_buffer->release(m_index);
|
||||||
blendop = video::EBO_ADD;
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case ParticleParamTypes::BlendMode::sub:
|
bool Particle::attachToBuffer(ParticleBuffer *buffer)
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
{
|
||||||
bfdst = video::EBF_DST_ALPHA;
|
auto index_opt = buffer->allocate();
|
||||||
blendop = video::EBO_REVSUBTRACT;
|
if (index_opt.has_value()) {
|
||||||
break;
|
m_index = index_opt.value();
|
||||||
|
m_buffer = buffer;
|
||||||
case ParticleParamTypes::BlendMode::screen:
|
return true;
|
||||||
bfsrc = video::EBF_ONE;
|
|
||||||
bfdst = video::EBF_ONE_MINUS_SRC_COLOR;
|
|
||||||
blendop = video::EBO_ADD;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // includes ParticleParamTypes::BlendMode::alpha
|
|
||||||
bfsrc = video::EBF_SRC_ALPHA;
|
|
||||||
bfdst = video::EBF_ONE_MINUS_SRC_ALPHA;
|
|
||||||
blendop = video::EBO_ADD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture
|
|
||||||
m_material.Lighting = false;
|
|
||||||
m_material.BackfaceCulling = false;
|
|
||||||
m_material.FogEnable = true;
|
|
||||||
m_material.forEachTexture([] (auto &tex) {
|
|
||||||
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
|
|
||||||
tex.MagFilter = video::ETMAGF_NEAREST;
|
|
||||||
});
|
|
||||||
|
|
||||||
// correctly render layered transparent particles -- see #10398
|
|
||||||
m_material.ZWriteEnable = video::EZW_AUTO;
|
|
||||||
|
|
||||||
// enable alpha blending and set blend mode
|
|
||||||
m_material.MaterialType = video::EMT_ONETEXTURE_BLEND;
|
|
||||||
m_material.MaterialTypeParam = video::pack_textureBlendFunc(
|
|
||||||
bfsrc, bfdst,
|
|
||||||
video::EMFN_MODULATE_1X,
|
|
||||||
video::EAS_TEXTURE | video::EAS_VERTEX_COLOR);
|
|
||||||
m_material.BlendOperation = blendop;
|
|
||||||
m_material.setTexture(0, m_texture.ref);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Irrlicht stuff
|
|
||||||
this->setAutomaticCulling(scene::EAC_OFF);
|
|
||||||
|
|
||||||
// Init lighting
|
|
||||||
updateLight();
|
|
||||||
|
|
||||||
// Init model
|
|
||||||
updateVertices();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::OnRegisterSceneNode()
|
void Particle::step(float dtime, ClientEnvironment *env)
|
||||||
{
|
|
||||||
if (IsVisible)
|
|
||||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
|
|
||||||
|
|
||||||
ISceneNode::OnRegisterSceneNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Particle::render()
|
|
||||||
{
|
|
||||||
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
|
||||||
driver->setMaterial(m_material);
|
|
||||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
|
||||||
|
|
||||||
u16 indices[] = {0,1,2, 2,3,0};
|
|
||||||
driver->drawVertexPrimitiveList(m_vertices, 4,
|
|
||||||
indices, 2, video::EVT_STANDARD,
|
|
||||||
scene::EPT_TRIANGLES, video::EIT_16BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Particle::step(float dtime)
|
|
||||||
{
|
{
|
||||||
m_time += dtime;
|
m_time += dtime;
|
||||||
|
|
||||||
@ -169,10 +100,10 @@ void Particle::step(float dtime)
|
|||||||
m_velocity = av*vecSign(m_velocity) + v3f(m_p.jitter.pickWithin())*dtime;
|
m_velocity = av*vecSign(m_velocity) + v3f(m_p.jitter.pickWithin())*dtime;
|
||||||
|
|
||||||
if (m_p.collisiondetection) {
|
if (m_p.collisiondetection) {
|
||||||
aabb3f box = m_collisionbox;
|
aabb3f box(v3f(-m_p.size / 2.0f), v3f(m_p.size / 2.0f));
|
||||||
v3f p_pos = m_pos * BS;
|
v3f p_pos = m_pos * BS;
|
||||||
v3f p_velocity = m_velocity * BS;
|
v3f p_velocity = m_velocity * BS;
|
||||||
collisionMoveResult r = collisionMoveSimple(m_env, m_gamedef, BS * 0.5f,
|
collisionMoveResult r = collisionMoveSimple(env, env->getGameDef(), BS * 0.5f,
|
||||||
box, 0.0f, dtime, &p_pos, &p_velocity, m_acceleration * BS, nullptr,
|
box, 0.0f, dtime, &p_pos, &p_velocity, m_acceleration * BS, nullptr,
|
||||||
m_p.object_collision);
|
m_p.object_collision);
|
||||||
|
|
||||||
@ -215,7 +146,7 @@ void Particle::step(float dtime)
|
|||||||
m_animation_time += dtime;
|
m_animation_time += dtime;
|
||||||
int frame_length_i = 0;
|
int frame_length_i = 0;
|
||||||
m_p.animation.determineParams(
|
m_p.animation.determineParams(
|
||||||
m_material.getTexture(0)->getSize(),
|
m_texture.ref->getSize(),
|
||||||
NULL, &frame_length_i, NULL);
|
NULL, &frame_length_i, NULL);
|
||||||
float frame_length = frame_length_i / 1000.0;
|
float frame_length = frame_length_i / 1000.0;
|
||||||
while (m_animation_time > frame_length) {
|
while (m_animation_time > frame_length) {
|
||||||
@ -225,23 +156,19 @@ void Particle::step(float dtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// animate particle alpha in accordance with settings
|
// animate particle alpha in accordance with settings
|
||||||
|
float alpha = 1.f;
|
||||||
if (m_texture.tex != nullptr)
|
if (m_texture.tex != nullptr)
|
||||||
m_alpha = m_texture.tex -> alpha.blend(m_time / (m_expiration+0.1f));
|
alpha = m_texture.tex -> alpha.blend(m_time / (m_expiration+0.1f));
|
||||||
else
|
|
||||||
m_alpha = 1.f;
|
|
||||||
|
|
||||||
// Update lighting
|
// Update lighting
|
||||||
updateLight();
|
auto col = updateLight(env);
|
||||||
|
col.setAlpha(255 * alpha);
|
||||||
|
|
||||||
// Update model
|
// Update model
|
||||||
updateVertices();
|
updateVertices(env, col);
|
||||||
|
|
||||||
// Update position -- see #10398
|
|
||||||
v3s16 camera_offset = m_env->getCameraOffset();
|
|
||||||
setPosition(m_pos*BS - intToFloat(camera_offset, BS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::updateLight()
|
video::SColor Particle::updateLight(ClientEnvironment *env)
|
||||||
{
|
{
|
||||||
u8 light = 0;
|
u8 light = 0;
|
||||||
bool pos_ok;
|
bool pos_ok;
|
||||||
@ -251,32 +178,37 @@ void Particle::updateLight()
|
|||||||
floor(m_pos.Y+0.5),
|
floor(m_pos.Y+0.5),
|
||||||
floor(m_pos.Z+0.5)
|
floor(m_pos.Z+0.5)
|
||||||
);
|
);
|
||||||
MapNode n = m_env->getClientMap().getNode(p, &pos_ok);
|
MapNode n = env->getClientMap().getNode(p, &pos_ok);
|
||||||
if (pos_ok)
|
if (pos_ok)
|
||||||
light = n.getLightBlend(m_env->getDayNightRatio(),
|
light = n.getLightBlend(env->getDayNightRatio(),
|
||||||
m_gamedef->ndef()->getLightingFlags(n));
|
env->getGameDef()->ndef()->getLightingFlags(n));
|
||||||
else
|
else
|
||||||
light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
|
light = blend_light(env->getDayNightRatio(), LIGHT_SUN, 0);
|
||||||
|
|
||||||
u8 m_light = decode_light(light + m_p.glow);
|
u8 m_light = decode_light(light + m_p.glow);
|
||||||
m_color.set(m_alpha*255,
|
return video::SColor(255,
|
||||||
m_light * m_base_color.getRed() / 255,
|
m_light * m_base_color.getRed() / 255,
|
||||||
m_light * m_base_color.getGreen() / 255,
|
m_light * m_base_color.getGreen() / 255,
|
||||||
m_light * m_base_color.getBlue() / 255);
|
m_light * m_base_color.getBlue() / 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::updateVertices()
|
void Particle::updateVertices(ClientEnvironment *env, video::SColor color)
|
||||||
{
|
{
|
||||||
f32 tx0, tx1, ty0, ty1;
|
f32 tx0, tx1, ty0, ty1;
|
||||||
v2f scale;
|
v2f scale;
|
||||||
|
|
||||||
|
if (!m_buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
video::S3DVertex *vertices = m_buffer->getVertices(m_index);
|
||||||
|
|
||||||
if (m_texture.tex != nullptr)
|
if (m_texture.tex != nullptr)
|
||||||
scale = m_texture.tex -> scale.blend(m_time / (m_expiration+0.1));
|
scale = m_texture.tex -> scale.blend(m_time / (m_expiration+0.1));
|
||||||
else
|
else
|
||||||
scale = v2f(1.f, 1.f);
|
scale = v2f(1.f, 1.f);
|
||||||
|
|
||||||
if (m_p.animation.type != TAT_NONE) {
|
if (m_p.animation.type != TAT_NONE) {
|
||||||
const v2u32 texsize = m_material.getTexture(0)->getSize();
|
const v2u32 texsize = m_texture.ref->getSize();
|
||||||
v2f texcoord, framesize_f;
|
v2f texcoord, framesize_f;
|
||||||
v2u32 framesize;
|
v2u32 framesize;
|
||||||
texcoord = m_p.animation.getTextureCoords(texsize, m_animation_frame);
|
texcoord = m_p.animation.getTextureCoords(texsize, m_animation_frame);
|
||||||
@ -297,31 +229,30 @@ void Particle::updateVertices()
|
|||||||
auto half = m_p.size * .5f,
|
auto half = m_p.size * .5f,
|
||||||
hx = half * scale.X,
|
hx = half * scale.X,
|
||||||
hy = half * scale.Y;
|
hy = half * scale.Y;
|
||||||
m_vertices[0] = video::S3DVertex(-hx, -hy,
|
vertices[0] = video::S3DVertex(-hx, -hy,
|
||||||
0, 0, 0, 0, m_color, tx0, ty1);
|
0, 0, 0, 0, color, tx0, ty1);
|
||||||
m_vertices[1] = video::S3DVertex(hx, -hy,
|
vertices[1] = video::S3DVertex(hx, -hy,
|
||||||
0, 0, 0, 0, m_color, tx1, ty1);
|
0, 0, 0, 0, color, tx1, ty1);
|
||||||
m_vertices[2] = video::S3DVertex(hx, hy,
|
vertices[2] = video::S3DVertex(hx, hy,
|
||||||
0, 0, 0, 0, m_color, tx1, ty0);
|
0, 0, 0, 0, color, tx1, ty0);
|
||||||
m_vertices[3] = video::S3DVertex(-hx, hy,
|
vertices[3] = video::S3DVertex(-hx, hy,
|
||||||
0, 0, 0, 0, m_color, tx0, ty0);
|
0, 0, 0, 0, color, tx0, ty0);
|
||||||
|
|
||||||
|
// Update position -- see #10398
|
||||||
|
auto *player = env->getLocalPlayer();
|
||||||
|
v3s16 camera_offset = env->getCameraOffset();
|
||||||
|
|
||||||
// see #10398
|
for (u16 i = 0; i < 4; i++) {
|
||||||
// v3s16 camera_offset = m_env->getCameraOffset();
|
video::S3DVertex &vertex = vertices[i];
|
||||||
// particle position is now handled by step()
|
|
||||||
m_box.reset(v3f());
|
|
||||||
|
|
||||||
for (video::S3DVertex &vertex : m_vertices) {
|
|
||||||
if (m_p.vertical) {
|
if (m_p.vertical) {
|
||||||
v3f ppos = m_player->getPosition()/BS;
|
v3f ppos = player->getPosition() / BS;
|
||||||
vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) /
|
vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) /
|
||||||
core::DEGTORAD + 90);
|
core::DEGTORAD + 90);
|
||||||
} else {
|
} else {
|
||||||
vertex.Pos.rotateYZBy(m_player->getPitch());
|
vertex.Pos.rotateYZBy(player->getPitch());
|
||||||
vertex.Pos.rotateXZBy(m_player->getYaw());
|
vertex.Pos.rotateXZBy(player->getYaw());
|
||||||
}
|
}
|
||||||
m_box.addInternalPoint(vertex.Pos);
|
vertex.Pos += m_pos * BS - intToFloat(camera_offset, BS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +261,6 @@ void Particle::updateVertices()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
ParticleSpawner::ParticleSpawner(
|
ParticleSpawner::ParticleSpawner(
|
||||||
IGameDef *gamedef,
|
|
||||||
LocalPlayer *player,
|
LocalPlayer *player,
|
||||||
const ParticleSpawnerParameters ¶ms,
|
const ParticleSpawnerParameters ¶ms,
|
||||||
u16 attached_id,
|
u16 attached_id,
|
||||||
@ -340,7 +270,6 @@ ParticleSpawner::ParticleSpawner(
|
|||||||
m_active(0),
|
m_active(0),
|
||||||
m_particlemanager(p_manager),
|
m_particlemanager(p_manager),
|
||||||
m_time(0.0f),
|
m_time(0.0f),
|
||||||
m_gamedef(gamedef),
|
|
||||||
m_player(player),
|
m_player(player),
|
||||||
p(params),
|
p(params),
|
||||||
m_texpool(std::move(texpool)),
|
m_texpool(std::move(texpool)),
|
||||||
@ -565,9 +494,6 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
|
|||||||
|
|
||||||
++m_active;
|
++m_active;
|
||||||
m_particlemanager->addParticle(std::make_unique<Particle>(
|
m_particlemanager->addParticle(std::make_unique<Particle>(
|
||||||
m_gamedef,
|
|
||||||
m_player,
|
|
||||||
env,
|
|
||||||
pp,
|
pp,
|
||||||
texture,
|
texture,
|
||||||
texpos,
|
texpos,
|
||||||
@ -624,6 +550,109 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ParticleBuffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
ParticleBuffer::ParticleBuffer(ClientEnvironment *env, const video::SMaterial &material)
|
||||||
|
: scene::ISceneNode(
|
||||||
|
env->getGameDef()->getSceneManager()->getRootSceneNode(),
|
||||||
|
env->getGameDef()->getSceneManager()),
|
||||||
|
m_mesh_buffer(make_irr<scene::SMeshBuffer>())
|
||||||
|
{
|
||||||
|
m_mesh_buffer->getMaterial() = material;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr u16 quad_indices[] = { 0, 1, 2, 2, 3, 0 };
|
||||||
|
|
||||||
|
std::optional<u16> ParticleBuffer::allocate()
|
||||||
|
{
|
||||||
|
u16 index;
|
||||||
|
|
||||||
|
m_usage_timer = 0;
|
||||||
|
|
||||||
|
if (!m_free_list.empty()) {
|
||||||
|
index = m_free_list.back();
|
||||||
|
m_free_list.pop_back();
|
||||||
|
auto *vertices = static_cast<video::S3DVertex*>(m_mesh_buffer->getVertices());
|
||||||
|
u16 *indices = m_mesh_buffer->getIndices();
|
||||||
|
// reset vertices, because it is only written in Particle::step()
|
||||||
|
for (u16 i = 0; i < 4; i++)
|
||||||
|
vertices[4 * index + i] = video::S3DVertex();
|
||||||
|
for (u16 i = 0; i < 6; i++)
|
||||||
|
indices[6 * index + i] = 4 * index + quad_indices[i];
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_count >= MAX_PARTICLES_PER_BUFFER)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
// append new vertices
|
||||||
|
// note: Our buffer never gets smaller, but ParticleManager will delete
|
||||||
|
// us after a while.
|
||||||
|
std::array<video::S3DVertex, 4> vertices {};
|
||||||
|
m_mesh_buffer->append(&vertices.front(), 4, quad_indices, 6);
|
||||||
|
index = m_count++;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleBuffer::release(u16 index)
|
||||||
|
{
|
||||||
|
assert(index < m_count);
|
||||||
|
u16 *indices = m_mesh_buffer->getIndices();
|
||||||
|
for (u16 i = 0; i < 6; i++)
|
||||||
|
indices[6 * index + i] = 0;
|
||||||
|
m_free_list.push_back(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
video::S3DVertex *ParticleBuffer::getVertices(u16 index)
|
||||||
|
{
|
||||||
|
if (index >= m_count)
|
||||||
|
return nullptr;
|
||||||
|
m_bounding_box_dirty = true;
|
||||||
|
return &(static_cast<video::S3DVertex *>(m_mesh_buffer->getVertices())[4 * index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleBuffer::OnRegisterSceneNode()
|
||||||
|
{
|
||||||
|
if (IsVisible)
|
||||||
|
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
|
||||||
|
scene::ISceneNode::OnRegisterSceneNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
const core::aabbox3df &ParticleBuffer::getBoundingBox() const
|
||||||
|
{
|
||||||
|
if (!m_bounding_box_dirty)
|
||||||
|
return m_mesh_buffer->BoundingBox;
|
||||||
|
|
||||||
|
core::aabbox3df box;
|
||||||
|
for (u16 i = 0; i < m_count; i++) {
|
||||||
|
// check if this index is used
|
||||||
|
static_assert(quad_indices[1] != 0);
|
||||||
|
if (m_mesh_buffer->getIndices()[6 * i + 1] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (u16 j = 0; j < 4; j++)
|
||||||
|
box.addInternalPoint(m_mesh_buffer->getPosition(i * 4 + j));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mesh_buffer->BoundingBox = box;
|
||||||
|
m_bounding_box_dirty = false;
|
||||||
|
return m_mesh_buffer->BoundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleBuffer::render()
|
||||||
|
{
|
||||||
|
video::IVideoDriver *driver = SceneManager->getVideoDriver();
|
||||||
|
|
||||||
|
if (isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
driver->setTransform(video::ETS_WORLD, core::matrix4());
|
||||||
|
driver->setMaterial(m_mesh_buffer->getMaterial());
|
||||||
|
driver->drawMeshBuffer(m_mesh_buffer.get());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ParticleManager
|
ParticleManager
|
||||||
*/
|
*/
|
||||||
@ -639,8 +668,9 @@ ParticleManager::~ParticleManager()
|
|||||||
|
|
||||||
void ParticleManager::step(float dtime)
|
void ParticleManager::step(float dtime)
|
||||||
{
|
{
|
||||||
stepParticles (dtime);
|
stepParticles(dtime);
|
||||||
stepSpawners (dtime);
|
stepSpawners(dtime);
|
||||||
|
stepBuffers(dtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticleManager::stepSpawners(float dtime)
|
void ParticleManager::stepSpawners(float dtime)
|
||||||
@ -684,35 +714,59 @@ void ParticleManager::stepParticles(float dtime)
|
|||||||
assert(parent->hasActive());
|
assert(parent->hasActive());
|
||||||
parent->decrActive();
|
parent->decrActive();
|
||||||
}
|
}
|
||||||
// remove scene node
|
|
||||||
p.remove();
|
|
||||||
// delete
|
// delete
|
||||||
m_particles[i] = std::move(m_particles.back());
|
m_particles[i] = std::move(m_particles.back());
|
||||||
m_particles.pop_back();
|
m_particles.pop_back();
|
||||||
} else {
|
} else {
|
||||||
p.step(dtime);
|
p.step(dtime, m_env);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticleManager::stepBuffers(float dtime)
|
||||||
|
{
|
||||||
|
constexpr float INTERVAL = 0.5f;
|
||||||
|
if (!m_buffer_gc.step(dtime, INTERVAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MutexAutoLock lock(m_particle_list_lock);
|
||||||
|
|
||||||
|
// remove buffers that have been unused for 5 seconds
|
||||||
|
size_t alloc = 0;
|
||||||
|
for (size_t i = 0; i < m_particle_buffers.size(); ) {
|
||||||
|
auto &buf = m_particle_buffers[i];
|
||||||
|
buf->m_usage_timer += INTERVAL;
|
||||||
|
if (buf->isEmpty() && buf->m_usage_timer > 5.0f) {
|
||||||
|
// delete and swap with last
|
||||||
|
buf->remove();
|
||||||
|
buf = std::move(m_particle_buffers.back());
|
||||||
|
m_particle_buffers.pop_back();
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
alloc += buf->m_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_profiler->avg("ParticleManager: particle buffer count [#]", m_particle_buffers.size());
|
||||||
|
if (!m_particle_buffers.empty())
|
||||||
|
g_profiler->avg("ParticleManager: buffer allocated size [#]", alloc);
|
||||||
|
}
|
||||||
|
|
||||||
void ParticleManager::clearAll()
|
void ParticleManager::clearAll()
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_spawner_list_lock);
|
MutexAutoLock lock(m_spawner_list_lock);
|
||||||
MutexAutoLock lock2(m_particle_list_lock);
|
MutexAutoLock lock2(m_particle_list_lock);
|
||||||
|
|
||||||
// clear particle spawners
|
|
||||||
m_particle_spawners.clear();
|
m_particle_spawners.clear();
|
||||||
m_dying_particle_spawners.clear();
|
m_dying_particle_spawners.clear();
|
||||||
|
|
||||||
// clear particles
|
|
||||||
for (std::unique_ptr<Particle> &p : m_particles) {
|
|
||||||
// remove scene node
|
|
||||||
p->remove();
|
|
||||||
// delete
|
|
||||||
p.reset();
|
|
||||||
}
|
|
||||||
m_particles.clear();
|
m_particles.clear();
|
||||||
|
|
||||||
|
// have to remove from scene first because it keeps a reference
|
||||||
|
for (auto &it : m_particle_buffers)
|
||||||
|
it->remove();
|
||||||
|
m_particle_buffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
|
void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
|
||||||
@ -744,7 +798,6 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
|
|||||||
|
|
||||||
addParticleSpawner(event->add_particlespawner.id,
|
addParticleSpawner(event->add_particlespawner.id,
|
||||||
std::make_unique<ParticleSpawner>(
|
std::make_unique<ParticleSpawner>(
|
||||||
client,
|
|
||||||
player,
|
player,
|
||||||
p,
|
p,
|
||||||
event->add_particlespawner.attached_id,
|
event->add_particlespawner.attached_id,
|
||||||
@ -785,7 +838,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
|
|||||||
p.size = oldsize;
|
p.size = oldsize;
|
||||||
|
|
||||||
if (texture.ref) {
|
if (texture.ref) {
|
||||||
addParticle(std::make_unique<Particle>(client, player, m_env,
|
addParticle(std::make_unique<Particle>(
|
||||||
p, texture, texpos, texsize, color, nullptr,
|
p, texture, texpos, texsize, color, nullptr,
|
||||||
std::move(texstore)));
|
std::move(texstore)));
|
||||||
}
|
}
|
||||||
@ -885,9 +938,6 @@ void ParticleManager::addNodeParticle(IGameDef *gamedef,
|
|||||||
);
|
);
|
||||||
|
|
||||||
addParticle(std::make_unique<Particle>(
|
addParticle(std::make_unique<Particle>(
|
||||||
gamedef,
|
|
||||||
player,
|
|
||||||
m_env,
|
|
||||||
p,
|
p,
|
||||||
ClientParticleTexRef(ref),
|
ClientParticleTexRef(ref),
|
||||||
texpos,
|
texpos,
|
||||||
@ -902,13 +952,104 @@ void ParticleManager::reserveParticleSpace(size_t max_estimate)
|
|||||||
m_particles.reserve(m_particles.size() + max_estimate);
|
m_particles.reserve(m_particles.size() + max_estimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticleManager::addParticle(std::unique_ptr<Particle> toadd)
|
video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTexRef &texture)
|
||||||
|
{
|
||||||
|
// translate blend modes to GL blend functions
|
||||||
|
video::E_BLEND_FACTOR bfsrc, bfdst;
|
||||||
|
video::E_BLEND_OPERATION blendop;
|
||||||
|
const auto blendmode = texture.tex ? texture.tex->blendmode :
|
||||||
|
ParticleParamTypes::BlendMode::alpha;
|
||||||
|
|
||||||
|
switch (blendmode) {
|
||||||
|
case ParticleParamTypes::BlendMode::add:
|
||||||
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
|
bfdst = video::EBF_DST_ALPHA;
|
||||||
|
blendop = video::EBO_ADD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParticleParamTypes::BlendMode::sub:
|
||||||
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
|
bfdst = video::EBF_DST_ALPHA;
|
||||||
|
blendop = video::EBO_REVSUBTRACT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParticleParamTypes::BlendMode::screen:
|
||||||
|
bfsrc = video::EBF_ONE;
|
||||||
|
bfdst = video::EBF_ONE_MINUS_SRC_COLOR;
|
||||||
|
blendop = video::EBO_ADD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // includes ParticleParamTypes::BlendMode::alpha
|
||||||
|
bfsrc = video::EBF_SRC_ALPHA;
|
||||||
|
bfdst = video::EBF_ONE_MINUS_SRC_ALPHA;
|
||||||
|
blendop = video::EBO_ADD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
video::SMaterial material;
|
||||||
|
|
||||||
|
// Texture
|
||||||
|
material.Lighting = false;
|
||||||
|
material.BackfaceCulling = false;
|
||||||
|
material.FogEnable = true;
|
||||||
|
material.forEachTexture([] (auto &tex) {
|
||||||
|
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
|
||||||
|
tex.MagFilter = video::ETMAGF_NEAREST;
|
||||||
|
});
|
||||||
|
|
||||||
|
// We don't have working transparency sorting. Disable Z-Write for
|
||||||
|
// correct results for clipped-alpha at least.
|
||||||
|
material.ZWriteEnable = video::EZW_OFF;
|
||||||
|
|
||||||
|
// enable alpha blending and set blend mode
|
||||||
|
material.MaterialType = video::EMT_ONETEXTURE_BLEND;
|
||||||
|
material.MaterialTypeParam = video::pack_textureBlendFunc(
|
||||||
|
bfsrc, bfdst,
|
||||||
|
video::EMFN_MODULATE_1X,
|
||||||
|
video::EAS_TEXTURE | video::EAS_VERTEX_COLOR);
|
||||||
|
material.BlendOperation = blendop;
|
||||||
|
assert(texture.ref);
|
||||||
|
material.setTexture(0, texture.ref);
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleManager::addParticle(std::unique_ptr<Particle> toadd)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_particle_list_lock);
|
MutexAutoLock lock(m_particle_list_lock);
|
||||||
|
|
||||||
m_particles.push_back(std::move(toadd));
|
auto material = getMaterialForParticle(toadd->getTextureRef());
|
||||||
}
|
|
||||||
|
|
||||||
|
ParticleBuffer *found = nullptr;
|
||||||
|
// simple shortcut when multiple particles of the same type get added
|
||||||
|
if (!m_particles.empty()) {
|
||||||
|
auto &last = m_particles.back();
|
||||||
|
if (last->getBuffer() && last->getBuffer()->getMaterial(0) == material)
|
||||||
|
found = last->getBuffer();
|
||||||
|
}
|
||||||
|
// search fitting buffer
|
||||||
|
if (!found) {
|
||||||
|
for (auto &buffer : m_particle_buffers) {
|
||||||
|
if (buffer->getMaterial(0) == material) {
|
||||||
|
found = buffer.get();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// or create a new one
|
||||||
|
if (!found) {
|
||||||
|
auto tmp = make_irr<ParticleBuffer>(m_env, material);
|
||||||
|
found = tmp.get();
|
||||||
|
m_particle_buffers.push_back(std::move(tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toadd->attachToBuffer(found)) {
|
||||||
|
infostream << "ParticleManager: buffer full, dropping particle" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_particles.push_back(std::move(toadd));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ParticleManager::addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd)
|
void ParticleManager::addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd)
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
#include "irrlichttypes_extrabloated.h"
|
#include "irrlichttypes_extrabloated.h"
|
||||||
#include "localplayer.h"
|
#include "irr_ptr.h"
|
||||||
#include "../particles.h"
|
#include "../particles.h"
|
||||||
|
|
||||||
struct ClientEvent;
|
struct ClientEvent;
|
||||||
@ -29,6 +30,10 @@ class ParticleManager;
|
|||||||
class ClientEnvironment;
|
class ClientEnvironment;
|
||||||
struct MapNode;
|
struct MapNode;
|
||||||
struct ContentFeatures;
|
struct ContentFeatures;
|
||||||
|
class LocalPlayer;
|
||||||
|
class ITextureSource;
|
||||||
|
class IGameDef;
|
||||||
|
class Client;
|
||||||
|
|
||||||
struct ClientParticleTexture
|
struct ClientParticleTexture
|
||||||
{
|
{
|
||||||
@ -38,9 +43,7 @@ struct ClientParticleTexture
|
|||||||
video::ITexture *ref = nullptr;
|
video::ITexture *ref = nullptr;
|
||||||
|
|
||||||
ClientParticleTexture() = default;
|
ClientParticleTexture() = default;
|
||||||
ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *t):
|
ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc);
|
||||||
tex(p),
|
|
||||||
ref(t->getTextureForMesh(p.string)) {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientParticleTexRef
|
struct ClientParticleTexRef
|
||||||
@ -61,14 +64,12 @@ struct ClientParticleTexRef
|
|||||||
};
|
};
|
||||||
|
|
||||||
class ParticleSpawner;
|
class ParticleSpawner;
|
||||||
|
class ParticleBuffer;
|
||||||
|
|
||||||
class Particle : public scene::ISceneNode
|
class Particle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Particle(
|
Particle(
|
||||||
IGameDef *gamedef,
|
|
||||||
LocalPlayer *player,
|
|
||||||
ClientEnvironment *env,
|
|
||||||
const ParticleParameters &p,
|
const ParticleParameters &p,
|
||||||
const ClientParticleTexRef &texture,
|
const ClientParticleTexRef &texture,
|
||||||
v2f texpos,
|
v2f texpos,
|
||||||
@ -78,61 +79,46 @@ public:
|
|||||||
std::unique_ptr<ClientParticleTexture> owned_texture = nullptr
|
std::unique_ptr<ClientParticleTexture> owned_texture = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
virtual const aabb3f &getBoundingBox() const
|
~Particle();
|
||||||
{
|
|
||||||
return m_box;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual u32 getMaterialCount() const
|
DISABLE_CLASS_COPY(Particle)
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual video::SMaterial& getMaterial(u32 i)
|
void step(float dtime, ClientEnvironment *env);
|
||||||
{
|
|
||||||
return m_material;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void OnRegisterSceneNode();
|
bool isExpired () const
|
||||||
virtual void render();
|
|
||||||
|
|
||||||
void step(float dtime);
|
|
||||||
|
|
||||||
bool isExpired ()
|
|
||||||
{ return m_expiration < m_time; }
|
{ return m_expiration < m_time; }
|
||||||
|
|
||||||
ParticleSpawner *getParent() { return m_parent; }
|
ParticleSpawner *getParent() const { return m_parent; }
|
||||||
|
|
||||||
|
const ClientParticleTexRef &getTextureRef() const { return m_texture; }
|
||||||
|
|
||||||
|
ParticleBuffer *getBuffer() const { return m_buffer; }
|
||||||
|
bool attachToBuffer(ParticleBuffer *buffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateLight();
|
video::SColor updateLight(ClientEnvironment *env);
|
||||||
void updateVertices();
|
void updateVertices(ClientEnvironment *env, video::SColor color);
|
||||||
void setVertexAlpha(float a);
|
|
||||||
|
ParticleBuffer *m_buffer = nullptr;
|
||||||
|
u16 m_index; // index in m_buffer
|
||||||
|
|
||||||
video::S3DVertex m_vertices[4];
|
|
||||||
float m_time = 0.0f;
|
float m_time = 0.0f;
|
||||||
float m_expiration;
|
float m_expiration;
|
||||||
|
|
||||||
ClientEnvironment *m_env;
|
// Color without lighting
|
||||||
IGameDef *m_gamedef;
|
video::SColor m_base_color;
|
||||||
aabb3f m_box;
|
|
||||||
aabb3f m_collisionbox;
|
|
||||||
ClientParticleTexRef m_texture;
|
ClientParticleTexRef m_texture;
|
||||||
video::SMaterial m_material;
|
|
||||||
v2f m_texpos;
|
v2f m_texpos;
|
||||||
v2f m_texsize;
|
v2f m_texsize;
|
||||||
v3f m_pos;
|
v3f m_pos;
|
||||||
v3f m_velocity;
|
v3f m_velocity;
|
||||||
v3f m_acceleration;
|
v3f m_acceleration;
|
||||||
const ParticleParameters m_p;
|
|
||||||
LocalPlayer *m_player;
|
|
||||||
|
|
||||||
//! Color without lighting
|
const ParticleParameters m_p;
|
||||||
video::SColor m_base_color;
|
|
||||||
//! Final rendered color
|
|
||||||
video::SColor m_color;
|
|
||||||
float m_animation_time = 0.0f;
|
float m_animation_time = 0.0f;
|
||||||
int m_animation_frame = 0;
|
int m_animation_frame = 0;
|
||||||
float m_alpha = 0.0f;
|
|
||||||
|
|
||||||
ParticleSpawner *m_parent = nullptr;
|
ParticleSpawner *m_parent = nullptr;
|
||||||
// Used if not spawned from a particlespawner
|
// Used if not spawned from a particlespawner
|
||||||
@ -142,8 +128,7 @@ private:
|
|||||||
class ParticleSpawner
|
class ParticleSpawner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ParticleSpawner(IGameDef *gamedef,
|
ParticleSpawner(LocalPlayer *player,
|
||||||
LocalPlayer *player,
|
|
||||||
const ParticleSpawnerParameters ¶ms,
|
const ParticleSpawnerParameters ¶ms,
|
||||||
u16 attached_id,
|
u16 attached_id,
|
||||||
std::vector<ClientParticleTexture> &&texpool,
|
std::vector<ClientParticleTexture> &&texpool,
|
||||||
@ -164,7 +149,6 @@ private:
|
|||||||
size_t m_active;
|
size_t m_active;
|
||||||
ParticleManager *m_particlemanager;
|
ParticleManager *m_particlemanager;
|
||||||
float m_time;
|
float m_time;
|
||||||
IGameDef *m_gamedef;
|
|
||||||
LocalPlayer *m_player;
|
LocalPlayer *m_player;
|
||||||
ParticleSpawnerParameters p;
|
ParticleSpawnerParameters p;
|
||||||
std::vector<ClientParticleTexture> m_texpool;
|
std::vector<ClientParticleTexture> m_texpool;
|
||||||
@ -172,12 +156,61 @@ private:
|
|||||||
u16 m_attached_id;
|
u16 m_attached_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ParticleBuffer : public scene::ISceneNode
|
||||||
|
{
|
||||||
|
friend class ParticleManager;
|
||||||
|
public:
|
||||||
|
ParticleBuffer(ClientEnvironment *env, const video::SMaterial &material);
|
||||||
|
|
||||||
|
// for pointer stability
|
||||||
|
DISABLE_CLASS_COPY(ParticleBuffer)
|
||||||
|
|
||||||
|
/// Reserves one more slot for a particle (4 vertices, 6 indices)
|
||||||
|
/// @return particle index within buffer
|
||||||
|
std::optional<u16> allocate();
|
||||||
|
/// Frees the particle at `index`
|
||||||
|
void release(u16 index);
|
||||||
|
|
||||||
|
/// @return video::S3DVertex[4]
|
||||||
|
video::S3DVertex *getVertices(u16 index);
|
||||||
|
|
||||||
|
inline bool isEmpty() const {
|
||||||
|
return m_free_list.size() == m_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual video::SMaterial &getMaterial(u32 num) override {
|
||||||
|
return m_mesh_buffer->getMaterial();
|
||||||
|
}
|
||||||
|
virtual u32 getMaterialCount() const override {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const core::aabbox3df &getBoundingBox() const override;
|
||||||
|
|
||||||
|
virtual void render() override;
|
||||||
|
|
||||||
|
virtual void OnRegisterSceneNode() override;
|
||||||
|
|
||||||
|
// we have 16-bit indices
|
||||||
|
static constexpr u16 MAX_PARTICLES_PER_BUFFER = 16000;
|
||||||
|
|
||||||
|
private:
|
||||||
|
irr_ptr<scene::SMeshBuffer> m_mesh_buffer;
|
||||||
|
// unused (e.g. expired) particle indices for re-use
|
||||||
|
std::vector<u16> m_free_list;
|
||||||
|
// for automatic deletion when unused for a while. is reset on allocate().
|
||||||
|
float m_usage_timer = 0;
|
||||||
|
// total count of contained particles
|
||||||
|
u16 m_count = 0;
|
||||||
|
mutable bool m_bounding_box_dirty = true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class doing particle as well as their spawners handling
|
* Class doing particle as well as their spawners handling
|
||||||
*/
|
*/
|
||||||
class ParticleManager
|
class ParticleManager
|
||||||
{
|
{
|
||||||
friend class ParticleSpawner;
|
friend class ParticleSpawner;
|
||||||
public:
|
public:
|
||||||
ParticleManager(ClientEnvironment* env);
|
ParticleManager(ClientEnvironment* env);
|
||||||
DISABLE_CLASS_COPY(ParticleManager)
|
DISABLE_CLASS_COPY(ParticleManager)
|
||||||
@ -213,7 +246,9 @@ protected:
|
|||||||
ParticleParameters &p, video::ITexture **texture, v2f &texpos,
|
ParticleParameters &p, video::ITexture **texture, v2f &texpos,
|
||||||
v2f &texsize, video::SColor *color, u8 tilenum = 0);
|
v2f &texsize, video::SColor *color, u8 tilenum = 0);
|
||||||
|
|
||||||
void addParticle(std::unique_ptr<Particle> toadd);
|
static video::SMaterial getMaterialForParticle(const ClientParticleTexRef &texture);
|
||||||
|
|
||||||
|
bool addParticle(std::unique_ptr<Particle> toadd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd);
|
void addParticleSpawner(u64 id, std::unique_ptr<ParticleSpawner> toadd);
|
||||||
@ -221,17 +256,23 @@ private:
|
|||||||
|
|
||||||
void stepParticles(float dtime);
|
void stepParticles(float dtime);
|
||||||
void stepSpawners(float dtime);
|
void stepSpawners(float dtime);
|
||||||
|
void stepBuffers(float dtime);
|
||||||
|
|
||||||
void clearAll();
|
void clearAll();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Particle>> m_particles;
|
std::vector<std::unique_ptr<Particle>> m_particles;
|
||||||
std::unordered_map<u64, std::unique_ptr<ParticleSpawner>> m_particle_spawners;
|
std::unordered_map<u64, std::unique_ptr<ParticleSpawner>> m_particle_spawners;
|
||||||
std::vector<std::unique_ptr<ParticleSpawner>> m_dying_particle_spawners;
|
std::vector<std::unique_ptr<ParticleSpawner>> m_dying_particle_spawners;
|
||||||
// Start the particle spawner ids generated from here after u32_max. lower values are
|
std::vector<irr_ptr<ParticleBuffer>> m_particle_buffers;
|
||||||
// for server sent spawners.
|
|
||||||
u64 m_next_particle_spawner_id = U32_MAX + 1;
|
// Start the particle spawner ids generated from here after u32_max.
|
||||||
|
// lower values are for server sent spawners.
|
||||||
|
u64 m_next_particle_spawner_id = static_cast<u64>(U32_MAX) + 1;
|
||||||
|
|
||||||
ClientEnvironment *m_env;
|
ClientEnvironment *m_env;
|
||||||
|
|
||||||
|
IntervalLimiter m_buffer_gc;
|
||||||
|
|
||||||
std::mutex m_particle_list_lock;
|
std::mutex m_particle_list_lock;
|
||||||
std::mutex m_spawner_list_lock;
|
std::mutex m_spawner_list_lock;
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ enum TileAnimationType : u8
|
|||||||
|
|
||||||
struct TileAnimationParams
|
struct TileAnimationParams
|
||||||
{
|
{
|
||||||
enum TileAnimationType type;
|
enum TileAnimationType type = TileAnimationType::TAT_NONE;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
// struct {
|
// struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user