Skip to main content

History: MeshEmitter

Source of version: 3 (current)

Copy to clipboard
            This plugin makes use of ((RetrieveVertexData|"Retrieving vertex data from a mesh")) function and credit should be given to all those involved in its creation.

WARNING: This code has not been subjected to vigorous testing and as such it may not operatate as expected. Use with caution.

{maketoc}

!CLEAN ME
Anyone is welcome to clean up this code.

!Usage
Create factory.

{CODE(wrap="1", colors="c++")}bool MyApplication::setup( void ) {
    mRoot = new Root();

        // MeshEmitter
        pMeshEmitFact = new MeshEmitterFactory();
        ParticleSystemManager::getSingleton().addEmitterFactory(pMeshEmitFact);

    .
    .
    .{CODE}

The mesh emitters take 4 additional parameters.

__mesh__ - String value.

File name of the mesh to be used.

__random__ - Boolean value.
default: true

If true the particles are emitted from random vertices otherwise the vertices are used sequentially.

__coverage__ - Real value between 0 and 1.

default: 0.0

The coverage dictates the percentage of vertices that will emit simultaneously.
i.e with a value 0.5 half the vertices emit simultaneously. 
A coverage of 0 will result in one vertex emitting in turn.

__use_vertex_normals__ - Boolean value.

default: true

Vertex normal is used to particles initial direction.

!!Example Emitter
An edited version of GreenyNimbus to use the ogrehead mesh as the emitter.

{CODE(wrap="1", colors="c++")}// Exudes greeny particles which float upwards
Examples/GreenyNimbus
{
    material        Examples/Flare
    particle_width  5
    particle_height 5
    cull_each       false
    quota           5000
    billboard_type  point
    
    // Area emitter
    emitter Mesh
    {
        angle           30
        emission_rate   50
        time_to_live    5
        direction       0 1 0
        velocity        7
        colour_range_start  1 1 0
        colour_range_end    0.3 1 0.3
        mesh        ogrehead.mesh
    random        false
    coverage    1.00
    use_vertex_normals    false
    }

    // Make em float upwards
    affector LinearForce
    {
        force_vector      0 6 0
        force_application add
    }

    // Fader
    affector ColourFader
    {
        red -0.25
        green -0.25
        blue -0.25
    }

}{CODE}

!MeshEmitter
!!MeshEmitter.h
{CODE(wrap="1", colors="c++")}#ifndef __MeshEmitter_H__
#define __MeshEmitter_H__

#include "OgreParticleFXPrerequisites.h"
#include "OgreParticleEmitter.h"
#include "OgreMesh.h"

namespace Ogre {

    class _OgreParticleFXExport MeshEmitter : public ParticleEmitter
    {
    public:
        /** Command object for random emitters (see ParamCommand).*/
        class CmdRandom : public ParamCommand
        {
        public:
            String doGet(const void* target) const;
            void doSet(void* target, const String& val);
        };
        /** Command object for percentage coverage emitters (see ParamCommand).*/
        class CmdCoverage : public ParamCommand
        {
        public:
            String doGet(const void* target) const;
            void doSet(void* target, const String& val);
        };
        /** Command object for using vertex normal directions (see ParamCommand).*/
        class CmdUseVertexNormals : public ParamCommand
        {
        public:
            String doGet(const void* target) const;
            void doSet(void* target, const String& val);
        };
        /** Command object for emitter mesh (see ParamCommand).*/
        class CmdMesh : public ParamCommand
        {
        public:
            String doGet(const void* target) const;
            void doSet(void* target, const String& val);
        };

        MeshEmitter(ParticleSystem* psys);// : AreaEmitter(psys) {}

        void genEmissionDirection(Vector3& destVector);
        unsigned short _getEmissionCount(Real timeElapsed);

        /** Overloaded to update the trans. matrix */
        void setDirection( const Vector3& direction );

        /** See ParticleEmitter. */
        void _initParticle(Particle* pParticle);

        void setRandom(bool random);
        bool getRandom(void) const;

        void setCoverage(Real coverge);
        Real getCoverage(void) const;

        void setUseVertexNormals(bool vertexNormals);
        bool getUseVertexNormals(void) const;

        /** Sets the name of mesh of the emitter. */
        void setMesh(String name);
        /** Gets the name of mesh of the emitter. */
        MeshPtr getMesh(void) const;
        String getMeshName(void) const;

    protected:
        String meshName;
        MeshPtr mesh;
    bool mRandom;
    bool mUseVertexNormals;
    Real mCoverage;
    size_t vertex_count,index_count;
    Vector3* vertices;
    Vector3* normals;
    unsigned* indices;

    int currentVertex;

    void getMeshInformation(MeshPtr mesh,size_t &vertex_count,Vector3* &vertices, Vector3* &normals, size_t &index_count, unsigned* &indices, const Vector3 &position = Vector3::ZERO,const Quaternion &orient = Quaternion::IDENTITY,const Vector3 &scale = Vector3::UNIT_SCALE);

        /// Command objects
        static CmdRandom msCmdRandom;
        static CmdCoverage msCmdCoverage;
        static CmdUseVertexNormals msCmdUseVertexNormals;
        static CmdMesh msCmdMesh;

    };

}

#endif{CODE}

!!MeshEmitter.cpp
{CODE(wrap="1", colors="c++")}#include "OgreMeshEmitter.h"
#include "OgreParticle.h"
#include "OgreQuaternion.h"
#include "OgreException.h"
#include "OgreStringConverter.h"
#include "OgreMeshManager.h"
#include "Ogre.h"

namespace Ogre {

    // Instatiate statics
    MeshEmitter::CmdMesh MeshEmitter::msCmdMesh;
    MeshEmitter::CmdRandom MeshEmitter::msCmdRandom;
    MeshEmitter::CmdCoverage MeshEmitter::msCmdCoverage;
    MeshEmitter::CmdUseVertexNormals MeshEmitter::msCmdUseVertexNormals;

    //-----------------------------------------------------------------------
    MeshEmitter::MeshEmitter(ParticleSystem* psys)
        : ParticleEmitter(psys)
    {
        mType = "Mesh";

        mDirection = Vector3::UNIT_Z;
        mUp = Vector3::UNIT_Z;

    currentVertex = 0;
    mRandom = true;
    mCoverage = 0.0;
    mUseVertexNormals = true;
        if (createParamDictionary("MeshEmitter"))
        {

            addBaseParameters();
            ParamDictionary* pDict = getParamDictionary();

            pDict->addParameter(ParameterDef("mesh", 
                "Mesh for use as emitter",
                PT_REAL),&msCmdMesh);
            pDict->addParameter(ParameterDef("random", 
                "vertices emit in random order",
                PT_BOOL),&msCmdRandom);
            pDict->addParameter(ParameterDef("coverage", 
                "percentage of vertices emitting",
                PT_BOOL),&msCmdCoverage);
            pDict->addParameter(ParameterDef("use_vertex_normals", 
                "get particle direction from vertex normal",
                PT_BOOL),&msCmdUseVertexNormals);
        }
    }

    void MeshEmitter::_initParticle(Particle* pParticle)
    {
        Vector3 offset;
    Vector3 scale;

    // Call superclass
        ParticleEmitter::_initParticle(pParticle);

    if (mRandom)
        currentVertex = rand() % vertex_count;
    else
    {
        currentVertex++;
        if (currentVertex==vertex_count)
            currentVertex = 0;
    }
    offset = vertices[currentVertex];

    scale = mParent->getParentNode()->getScale();
    pParticle->position = mPosition + (offset * scale);
    

    // Generate complex data by reference
    genEmissionColour(pParticle->colour);
    genEmissionDirection(pParticle->direction);
    genEmissionVelocity(pParticle->direction);

    // Generate simpler data
    pParticle->timeToLive = pParticle->totalTimeToLive = genEmissionTTL();
    }

    //-----------------------------------------------------------------------
    void MeshEmitter::setRandom(bool random)
    {
        mRandom = random;
    }
    //-----------------------------------------------------------------------
    bool MeshEmitter::getRandom(void) const
    {
        return mRandom;
    }
    //-----------------------------------------------------------------------
    void MeshEmitter::setCoverage(Real coverage)
    {
        mCoverage = coverage;
    }
    //-----------------------------------------------------------------------
    Real MeshEmitter::getCoverage(void) const
    {
        return mCoverage;
    }
    //-----------------------------------------------------------------------
    void MeshEmitter::setUseVertexNormals(bool vertexNormals)
    {
        mUseVertexNormals = vertexNormals;
    }
    //-----------------------------------------------------------------------
    bool MeshEmitter::getUseVertexNormals(void) const
    {
        return mUseVertexNormals;
    }
    //-----------------------------------------------------------------------
    void MeshEmitter::setMesh(String name)
    {
    meshName = name;
    mesh = MeshManager::getSingleton().load(name, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
    getMeshInformation(mesh,vertex_count,vertices,normals,index_count,indices);
    }
    //-----------------------------------------------------------------------
    String MeshEmitter::getMeshName(void) const
    {
        return meshName;
    }
    //-----------------------------------------------------------------------
    MeshPtr MeshEmitter::getMesh(void) const
    {
        return mesh;
    }
    //-----------------------------------------------------------------------
    void MeshEmitter::genEmissionDirection(Vector3& destVector)
    {
    if (mUseVertexNormals)
    {
        if (mAngle != Radian(0))
        {
        // Randomise angle
        Radian angle = Math::UnitRandom() * mAngle;
    
        // Randomise direction
        destVector = normals[currentVertex].randomDeviant(angle, mUp);
        }
        else
        {
        // Constant angle
        destVector = normals[currentVertex];
        }
    }
    else
        ParticleEmitter::genEmissionDirection(destVector);
    }
    //-----------------------------------------------------------------------
    unsigned short MeshEmitter::_getEmissionCount(Real timeElapsed)
    {
        // Use basic constant emission 
    unsigned short mod = vertex_count*mCoverage;
    if (mod==0)
        mod = 1;
        return genConstantEmissionCount(timeElapsed)*mod;
    }
    //-----------------------------------------------------------------------
    void MeshEmitter::setDirection( const Vector3& direction )
    {
        ParticleEmitter::setDirection( direction );

    }

    //-----------------------------------------------------------------------
    // Command objects
    //-----------------------------------------------------------------------
    //-----------------------------------------------------------------------
    String MeshEmitter::CmdRandom::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const MeshEmitter*>(target)->getRandom() );
    }
    void MeshEmitter::CmdRandom::doSet(void* target, const String& val)
    {
        static_cast<MeshEmitter*>(target)->setRandom(StringConverter::parseBool(val));
    }
    //-----------------------------------------------------------------------
    String MeshEmitter::CmdCoverage::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const MeshEmitter*>(target)->getCoverage() );
    }
    void MeshEmitter::CmdCoverage::doSet(void* target, const String& val)
    {
        static_cast<MeshEmitter*>(target)->setCoverage(StringConverter::parseReal(val));
    }
    //-----------------------------------------------------------------------
    String MeshEmitter::CmdUseVertexNormals::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const MeshEmitter*>(target)->getUseVertexNormals() );
    }
    void MeshEmitter::CmdUseVertexNormals::doSet(void* target, const String& val)
    {
        static_cast<MeshEmitter*>(target)->setUseVertexNormals(StringConverter::parseBool(val));
    }
    //-----------------------------------------------------------------------
    String MeshEmitter::CmdMesh::doGet(const void* target) const
    {
        return static_cast<const MeshEmitter*>(target)->getMeshName();
    }
    void MeshEmitter::CmdMesh::doSet(void* target, const String& val)
    {
        static_cast<MeshEmitter*>(target)->setMesh(val);
    }

    void MeshEmitter::getMeshInformation(MeshPtr mesh,size_t &vertex_count,Vector3* &vertices, Vector3* &normals, size_t &index_count, unsigned* &indices, const Vector3 &position,const Quaternion &orient,const Vector3 &scale)
    {
        vertex_count = index_count = 0;
        
        bool added_shared = false;
        size_t current_offset = vertex_count;
        size_t shared_offset = vertex_count;
        size_t next_offset = vertex_count;
        size_t index_offset = index_count;
        size_t prev_vert = vertex_count;
        size_t prev_ind = index_count;
        
        // Calculate how many vertices and indices we're going to need
        for(int i = 0;i < mesh->getNumSubMeshes();i++)
        {
            SubMesh* submesh = mesh->getSubMesh(i);
        
            // We only need to add the shared vertices once
            if(submesh->useSharedVertices)
            {
            if(!added_shared)
            {
                VertexData* vertex_data = mesh->sharedVertexData;
                vertex_count += vertex_data->vertexCount;
                added_shared = true;
            }
            }
            else
            {
            VertexData* vertex_data = submesh->vertexData;
            vertex_count += vertex_data->vertexCount;
            }
        
            // Add the indices
            Ogre::IndexData* index_data = submesh->indexData;
            index_count += index_data->indexCount;
        }
        
        // Allocate space for the vertices and indices
        vertices = new Vector3[vertex_count];
        normals = new Vector3[vertex_count];
        indices = new unsigned[index_count];
        
        added_shared = false;
        
        // Run through the submeshes again, adding the data into the arrays
        for(int i = 0;i < mesh->getNumSubMeshes();i++)
        {
            SubMesh* submesh = mesh->getSubMesh(i);
        
            Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
            if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
            {
            if(submesh->useSharedVertices)
            {
                added_shared = true;
                shared_offset = current_offset;
            }
        
            const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
            const Ogre::VertexElement* normalElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_NORMAL);
            Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
            unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
            Ogre::Real* pReal;
        
            for(size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
            {
                posElem->baseVertexPointerToElement(vertex, &pReal);
        
                Vector3 pt;
        
                pt.x = (*pReal++);
                pt.y = (*pReal++);
                pt.z = (*pReal++);
        
                pt = (orient * (pt * scale)) + position;
        
                vertices[current_offset + j].x = pt.x;
                vertices[current_offset + j].y = pt.y;
                vertices[current_offset + j].z = pt.z;

                normalElem->baseVertexPointerToElement(vertex, &pReal);
                pt.x = (*pReal++);
                pt.y = (*pReal++);
                pt.z = (*pReal++);

                normals[current_offset + j].x = pt.x;
                normals[current_offset + j].y = pt.y;
                normals[current_offset + j].z = pt.z;

            }
            vbuf->unlock();
            next_offset += vertex_data->vertexCount;
            }

            Ogre::IndexData* index_data = submesh->indexData;
        
            size_t numTris = index_data->indexCount / 3;
            unsigned short* pShort;
            unsigned int* pInt;
            Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
            bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
            if (use32bitindexes) pInt = static_cast<unsigned int*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
            else pShort = static_cast<unsigned short*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
        
            for(size_t k = 0; k < numTris; ++k)
            {
            size_t offset = (submesh->useSharedVertices)?shared_offset:current_offset;
        
            unsigned int vindex = use32bitindexes? *pInt++ : *pShort++;
            indices[index_offset + 0] = vindex + offset;
            vindex = use32bitindexes? *pInt++ : *pShort++;
            indices[index_offset + 1] = vindex + offset;
            vindex = use32bitindexes? *pInt++ : *pShort++;
            indices[index_offset + 2] = vindex + offset;
        
            index_offset += 3;
            }
            ibuf->unlock();
            current_offset = next_offset;
        }
    }

}{CODE}