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
        {
            if (m_is_glsl)
                m_node = ParticleSystemProxy::addParticleNode(m_is_glsl, type->randomizeInitialY());
            else
                m_node = irr_driver->addParticleNode();
            
            if (m_is_glsl)
            {
                bool additive = (type->getMaterial()->getShaderType() == Material::SHADERTYPE_ADDITIVE);
                static_cast<ParticleSystemProxy *>(m_node)->setAlphaAdditive(additive);
            }
        }

        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

            // fallback for old render engine
            if (material->getShaderType() == Material::SHADERTYPE_ADDITIVE)
                mat0.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
            else if (material->getShaderType() == Material::SHADERTYPE_ALPHA_BLEND)
                mat0.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
            else if (material->getShaderType() == Material::SHADERTYPE_ALPHA_TEST)
                mat0.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
        }
        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()
                                                     );

    #if VISUALIZE_BOX_EMITTER
                if (m_parent != NULL)
                {
                    for (int x=0; x<2; x++)
                    {
                        for (int y=0; y<2; y++)
                        {
                            for (int z=0; z<2; z++)
                            {
                                m_visualisation.push_back(
                                irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1,
                                                                                   core::vector3df((x ? box_size_x : -box_size_x),
                                                                                                   (y ? box_size_y : -box_size_y),
                                                                                                   -0.6 - (z ? 0 : type->getBoxSizeZ())))
                                                          );
                            }
                        }
                    }
                }
    #endif
                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

        scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255),
                                                                                    type->getFadeoutTime());
        m_node->addAffector(af);
        af->drop();

        if (type->getGravityStrength() != 0)
        {
            scene::IParticleGravityAffector *gaf = m_node->createGravityAffector(core::vector3df(00.0f, type->getGravityStrength(), 0.0f),
                                                                                 type->getForceLostToGravityTime());
            m_node->addAffector(gaf);
            gaf->drop();
        }

        const float fas = type->getFadeAwayStart();
        const float fae = type->getFadeAwayEnd();
        if (fas > 0.0f && fae > 0.0f)
        {
            FadeAwayAffector* faa = new FadeAwayAffector(fas*fas, fae*fae);
            m_node->addAffector(faa);
            faa->drop();
        }

        if (type->hasScaleAffector())
        {
            if (m_is_glsl)
            {
                static_cast<ParticleSystemProxy *>(m_node)->setIncreaseFactor(type->getScaleAffectorFactorX());
            }
            else
            {
                core::vector2df factor = core::vector2df(type->getScaleAffectorFactorX(),
                    type->getScaleAffectorFactorY());
                scene::IParticleAffector* scale_affector = new ScaleAffector(factor);
                m_node->addAffector(scale_affector);
                scale_affector->drop();
            }
        }

        if (type->getMinColor() != type->getMaxColor())
        {
            if (m_is_glsl)
            {
                video::SColor color_from = type->getMinColor();
                static_cast<ParticleSystemProxy *>(m_node)->setColorFrom(color_from.getRed() / 255.0f,
                    color_from.getGreen() / 255.0f,
                    color_from.getBlue() / 255.0f);

                video::SColor color_to = type->getMaxColor();
                static_cast<ParticleSystemProxy *>(m_node)->setColorTo(color_to.getRed() / 255.0f,
                    color_to.getGreen() / 255.0f,
                    color_to.getBlue() / 255.0f);
            }
            else
            {
                video::SColor color_from = type->getMinColor();
                core::vector3df color_from_v =
                    core::vector3df(float(color_from.getRed()),
                                    float(color_from.getGreen()),
                                    float(color_from.getBlue()));

                video::SColor color_to = type->getMaxColor();
                core::vector3df color_to_v = core::vector3df(float(color_to.getRed()),
                                                             float(color_to.getGreen()),
                                                             float(color_to.getBlue()));

                ColorAffector* affector = new ColorAffector(color_from_v, color_to_v);
                m_node->addAffector(affector);
                affector->drop();
            }
        }

        const float windspeed = type->getWindSpeed();
        if (windspeed > 0.01f)
        {
            WindAffector *waf = new WindAffector(windspeed);
            m_node->addAffector(waf);
            waf->drop();

            // TODO: wind affector for GLSL particles
        }

        const bool flips = type->getFlips();
        if (flips)
        {
            if (m_is_glsl)
                static_cast<ParticleSystemProxy *>(m_node)->setFlip();
        }
    }
}   // setParticleType
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 = irr_driver->addParticleNode();
        }
        
        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
        {
            m_node->setMaterialTexture(0, irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str()));
        }
        

        switch (type->getShape())
        {
            case EMITTER_POINT:
            {
                m_emitter = m_node->createPointEmitter(core::vector3df(m_particle_type->getVelocityX(),
                                                                       m_particle_type->getVelocityY(),
                                                                       m_particle_type->getVelocityZ()),   // velocity in m/ms
                                                       type->getMinRate(),  type->getMaxRate(),
                                                       type->getMinColor(), type->getMaxColor(),
                                                       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()),
                                                     core::vector3df(m_particle_type->getVelocityX(),
                                                                     m_particle_type->getVelocityY(),
                                                                     m_particle_type->getVelocityZ()),   // velocity in m/ms
                                                     type->getMinRate(),  type->getMaxRate(),
                                                     type->getMinColor(), type->getMaxColor(),
                                                     lifeTimeMin, lifeTimeMax,
                                                     m_particle_type->getAngleSpread() /* angle */
                                                     );
                
    #if VISUALIZE_BOX_EMITTER
                if (m_parent != NULL)
                {
                    for (int x=0; x<2; x++)
                    {
                        for (int y=0; y<2; y++)
                        {
                            for (int z=0; z<2; z++)
                            {
                                m_visualisation.push_back(
                                irr_driver->getSceneManager()->addSphereSceneNode(0.05f, 16, m_parent, -1,
                                                                                   core::vector3df((x ? box_size_x : -box_size_x),
                                                                                                   (y ? box_size_y : -box_size_y),
                                                                                                   -0.6 - (z ? 0 : type->getBoxSizeZ())))
                                                          );
                            }
                        }
                    }
                }
    #endif
                break;
            }
            default:
            {
                fprintf(stderr, "[ParticleEmitter] Unknown shape\n");
                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
        
        scene::IParticleFadeOutAffector *af = m_node->createFadeOutParticleAffector(video::SColor(0, 255, 255, 255),
                                                                                    type->getFadeoutTime());
        m_node->addAffector(af);
        af->drop();
        
        if (type->getGravityStrength() != 0)
        {
            scene::IParticleGravityAffector *gaf = m_node->createGravityAffector(core::vector3df(00.0f, type->getGravityStrength(), 0.0f),
                                                                                 type->getForceLostToGravityTime());
            m_node->addAffector(gaf);
            gaf->drop();
        }
        
        const float fas = type->getFadeAwayStart();
        const float fae = type->getFadeAwayEnd();
        if (fas > 0.0f && fae > 0.0f)
        {
            FadeAwayAffector* faa = new FadeAwayAffector(fas*fas, fae*fae);
            m_node->addAffector(faa);
            faa->drop();
        }
    }
}   // setParticleType