stk-code_catmod/src/graphics/particle_emitter.cpp
2017-10-15 11:32:58 +08:00

356 lines
13 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2011-2015 Joerg Henrichs, Marianne Gagnon
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef SERVER_ONLY
#include "graphics/particle_emitter.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/material.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/particle_kind.hpp"
#include "graphics/stk_particle.hpp"
#include "graphics/wind.hpp"
#include "io/file_manager.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
#include "utils/helpers.hpp"
#include <algorithm>
//-----------------------------------------------------------------------------
ParticleEmitter::ParticleEmitter(const ParticleKind* type,
const Vec3 &position,
scene::ISceneNode* parent,
bool randomize_initial_y,
bool important)
: m_position(position)
{
assert(type != NULL);
m_magic_number = 0x58781325;
m_node = NULL;
m_emitter = NULL;
m_particle_type = NULL;
m_parent = parent;
m_emission_decay_rate = 0;
m_randomize_initial_y = randomize_initial_y;
m_important = important;
setParticleType(type);
assert(m_node != NULL);
} // ParticleEmitter
//-----------------------------------------------------------------------------
/** Destructor, removes
*/
ParticleEmitter::~ParticleEmitter()
{
assert(m_magic_number == 0x58781325);
if (m_node != NULL)
irr_driver->removeNode(m_node);
m_emitter->drop();
m_magic_number = 0xDEADBEEF;
} // ~ParticleEmitter
//-----------------------------------------------------------------------------
void ParticleEmitter::update(float dt)
{
assert(m_magic_number == 0x58781325);
// No particles to emit, nothing to do
if (m_emitter->getMinParticlesPerSecond() == 0) return;
// the emission direction does not automatically follow the orientation of
// the node so fix that manually...
core::matrix4 transform = m_node->getAbsoluteTransformation();
core::vector3df velocity(m_particle_type->getVelocityX(),
m_particle_type->getVelocityY(),
m_particle_type->getVelocityZ());
transform.rotateVect(velocity);
m_emitter->setDirection(velocity);
if (m_emission_decay_rate > 0)
{
m_max_rate = m_min_rate = std::max(0.0f, (m_min_rate - m_emission_decay_rate*dt));
setCreationRateAbsolute(m_min_rate);
}
// There seems to be no way to randomise the velocity for particles,
// so we have to do this manually, by changing the default velocity.
// Irrlicht expects velocity (called 'direction') in m/ms!!
/*
const int x = m_particle_type->getAngleSpreadX();
const int y = m_particle_type->getAngleSpreadY();
const int z = m_particle_type->getAngleSpreadZ();
Vec3 dir(cos(DEGREE_TO_RAD*(rand()%x - x/2))*m_particle_type->getVelocityX(),
sin(DEGREE_TO_RAD*(rand()%y - x/2))*m_particle_type->getVelocityY(),
sin(DEGREE_TO_RAD*(rand()%z - x/2))*m_particle_type->getVelocityZ());
m_emitter->setDirection(dir.toIrrVector());
*/
} // update
//-----------------------------------------------------------------------------
/** Sets the creation rate as a relative fraction between minimum (f=0) and
* maximum (f=1) of the creation rates defined in the particle kind.
* \param fraction Fraction to use.
*/
void ParticleEmitter::setCreationRateRelative(float fraction)
{
assert(fraction >= 0.0f);
assert(fraction <= 1.0f);
const float min_rate = (float)(m_particle_type->getMinRate());
const float max_rate = (float)(m_particle_type->getMaxRate());
setCreationRateAbsolute(min_rate + fraction*(max_rate - min_rate));
} // setCreationRateRelative
//-----------------------------------------------------------------------------
/** Sets the absolute creation rate (in particles per second).
* \param f The creation rate (in particles per second).
*/
void ParticleEmitter::setCreationRateAbsolute(float f)
{
m_emitter->setMinParticlesPerSecond(int(f));
m_emitter->setMaxParticlesPerSecond(int(f));
m_min_rate = f;
m_max_rate = f;
} // setCreationRateAbsolute
//-----------------------------------------------------------------------------
int ParticleEmitter::getCreationRate()
{
if (m_node->getEmitter() == NULL) return 0;
return m_emitter->getMinParticlesPerSecond();
}
//-----------------------------------------------------------------------------
/** Sets the position of the particle emitter.
* \param pos The position for the particle emitter.
*/
void ParticleEmitter::setPosition(const Vec3 &pos)
{
m_node->setPosition(pos.toIrrVector());
} // setPosition
//-----------------------------------------------------------------------------
/** Sets the rotation of the particle emitter.
* \param pos The rotation for the particle emitter.
*/
void ParticleEmitter::setRotation(const Vec3 &rot)
{
m_node->setRotation(rot.toIrrVector());
} // setRotation
//-----------------------------------------------------------------------------
void ParticleEmitter::setParticleType(const ParticleKind* type)
{
assert(m_magic_number == 0x58781325);
bool is_new_type = (m_particle_type != type);
if (is_new_type)
{
if (m_node != NULL)
{
m_node->removeAll();
m_node->removeAllAffectors();
m_emitter->drop();
}
else
{
m_node = new STKParticle(type->randomizeInitialY());
}
if (m_parent != NULL)
{
m_node->setParent(m_parent);
}
m_particle_type = type;
}
m_emission_decay_rate = type->getEmissionDecayRate();
Material* material = type->getMaterial();
const float minSize = type->getMinSize();
const float maxSize = type->getMaxSize();
const int lifeTimeMin = type->getMinLifetime();
const int lifeTimeMax = type->getMaxLifetime();
assert(maxSize >= minSize);
assert(lifeTimeMax >= lifeTimeMin);
#ifdef DEBUG
if (material != NULL)
{
video::ITexture* tex = material->getTexture();
assert(tex != NULL);
const io::SNamedPath& name = tex->getName();
const io::path& tpath = name.getPath();
std::string debug_name = std::string("particles(") + tpath.c_str() + ")";
m_node->setName(debug_name.c_str());
}
#endif
m_min_rate = (float)type->getMinRate();
m_max_rate = (float)type->getMaxRate();
if (is_new_type)
{
video::SMaterial& mat0 = m_node->getMaterial(0);
m_node->setPosition(m_position.toIrrVector());
if (material != NULL)
{
assert(material->getTexture() != NULL);
material->setMaterialProperties(&mat0, NULL);
m_node->setMaterialTexture(0, material->getTexture());
mat0.ZWriteEnable = !material->isTransparent(); // disable z-buffer writes if material is transparent
}
else
{
std::string help = file_manager->getAsset(FileManager::GUI, "main_help.png");
m_node->setMaterialTexture(0, irr_driver->getTexture(help));
}
// velocity in m/ms
core::vector3df velocity(m_particle_type->getVelocityX(),
m_particle_type->getVelocityY(),
m_particle_type->getVelocityZ());
switch (type->getShape())
{
case EMITTER_POINT:
{
m_emitter = m_node->createPointEmitter(velocity,
type->getMinRate(), type->getMaxRate(),
type->getMinColor(), type->getMinColor(),
lifeTimeMin, lifeTimeMax,
m_particle_type->getAngleSpread() /* angle */
);
break;
}
case EMITTER_BOX:
{
const float box_size_x = type->getBoxSizeX()/2.0f;
const float box_size_y = type->getBoxSizeY()/2.0f;
m_emitter = m_node->createBoxEmitter(core::aabbox3df(-box_size_x, -box_size_y, -0.6f,
box_size_x, box_size_y, -0.6f - type->getBoxSizeZ()),
velocity,
type->getMinRate(), type->getMaxRate(),
type->getMinColor(), type->getMinColor(),
lifeTimeMin, lifeTimeMax,
m_particle_type->getAngleSpread()
);
break;
}
case EMITTER_SPHERE:
{
m_emitter = m_node->createSphereEmitter(core::vector3df(0.0f,0.0f,0.0f) /* center */,
m_particle_type->getSphereRadius(),
velocity,
type->getMinRate(), type->getMaxRate(),
type->getMinColor(), type->getMinColor(),
lifeTimeMin, lifeTimeMax,
m_particle_type->getAngleSpread()
);
break;
}
default:
{
Log::error("ParticleEmitter", "Unknown shape");
return;
}
}
}
else
{
m_emitter->setMinParticlesPerSecond(int(m_min_rate));
m_emitter->setMaxParticlesPerSecond(int(m_max_rate));
}
m_emitter->setMinStartSize(core::dimension2df(minSize, minSize));
m_emitter->setMaxStartSize(core::dimension2df(maxSize, maxSize));
if (is_new_type)
{
m_node->setEmitter(m_emitter); // this grabs the emitter
if (type->hasScaleAffector())
{
m_node->setIncreaseFactor(type->getScaleAffectorFactorX());
}
if (type->getMinColor() != type->getMaxColor())
{
video::SColor color_from = type->getMinColor();
m_node->setColorFrom(color_from.getRed() / 255.0f,
color_from.getGreen() / 255.0f,
color_from.getBlue() / 255.0f);
video::SColor color_to = type->getMaxColor();
m_node->setColorTo(color_to.getRed() / 255.0f,
color_to.getGreen() / 255.0f,
color_to.getBlue() / 255.0f);
}
const bool flips = type->getFlips();
if (flips)
{
m_node->setFlips();
}
}
} // setParticleType
//-----------------------------------------------------------------------------
void ParticleEmitter::addHeightMapAffector(Track* t)
{
const Vec3* aabb_min;
const Vec3* aabb_max;
t->getAABB(&aabb_min, &aabb_max);
float track_x = aabb_min->getX();
float track_z = aabb_min->getZ();
const float track_x_len = aabb_max->getX() - aabb_min->getX();
const float track_z_len = aabb_max->getZ() - aabb_min->getZ();
std::vector<std::vector<float> > array = t->buildHeightMap();
m_node->setHeightmap(array, track_x, track_z, track_x_len, track_z_len);
}
//-----------------------------------------------------------------------------
void ParticleEmitter::resizeBox(float size)
{
scene::IParticleBoxEmitter* emitter = (scene::IParticleBoxEmitter*)m_emitter;
const float box_size_x = m_particle_type->getBoxSizeX()/2.0f;
const float box_size_y = m_particle_type->getBoxSizeY()/2.0f;
emitter->setBox( core::aabbox3df(-box_size_x, -box_size_y, -0.6f,
box_size_x, box_size_y, -0.6f - size) );
}
#endif // !SERVER_ONLY