Creating custom MovableObject

bleubleu

11-04-2007 23:41:46

Hi!

I'm migrating my little SoundManager from C++ to C#.

My "sound" class inherits from Ogre::MovableObject so I can attach it to a node. It's neat because my sounds can move around the scene and it's really easy to compute stuff like velocity, etc..

Anyways, if I do :


public class Sound : Mogre.MovableObject
{
public Sound()
{
//...
}

// Here I would need to override some methods/properties like _updateRenderQueue(), getBoundingBox(), etc...

};


I get :

error CS0143: The type 'Mogre.MovableObject' has no constructors defined

which make sense because it does not have a constructor. How can I work around that ?

Thanks! Again, love the wrapper.

Mat

bleubleu

19-04-2007 19:05:44

I just want to keep the thread alive. I know its lame, but I need a reply from Bekas badly!

Thanks!

Mat

grizzley90

19-04-2007 19:48:48

I don't believe you can create managed plugins and get ogre to use them yet... (No managed scene managers etc...). You can use MogreFreeSL for now if you would like? It offers a lot of features like 3d sound, streaming, loading from zip, eax etc...

bleubleu

19-04-2007 19:53:41

I know what you mean, but I have not really trying to create a full-featured plugin, just inherit from MovableObject...

About FreeSL, thanks for the offer, but I already coded everything with FMOD and I am too lazy to switch. It has all the features I need.

Mat

Bekas

19-04-2007 21:52:03

Can you post your full C++ Sound class ? (mainly I'm interested in the MovableObject methods that you override/use)

bleubleu

19-04-2007 22:13:21

Why not!

It's very minimalistic. Just the functions I needed. I dont know if it can help you, but here it is. Sorry for the french comment, its a french project :)
  1. Sound are movable objects, so velocity and position can be computed at each time step. [/*:m]
  2. Direct occlusion is approximated very basically by ray shooting trough the scene (i didnt have time to use FMOD geometric function which can compute accurate direct/indirect occlusion). [/*:m]
  3. The manager can be set in "automatic" mode so it updates itself at each frame.[/*:m][/list:u]
    SoundManager.h


    #ifndef SOUNDMANAGER_H
    #define SOUNDMANAGER_H

    #include "SimPreReqs.h"

    namespace Sim
    {

    /**
    * Représente un sound qui peut se déplacer dans un envrionment 3D.
    * Ce sont dérive du MovableObject de Ogre pour pouvoir l'attacher a un
    * noeud de la scene.
    */
    class SIM_DLLEXPORT Sound : public Ogre::MovableObject
    {
    friend class SoundManager;

    public:

    /** Type de son. */
    enum SoundType
    {
    /** Son 2D. */
    ST_2D,
    /** Son 3D. */
    ST_3D
    };

    /** Type d'attenuation avec la distance. */
    enum RolloffType
    {
    /** Diminue lineairement avec la distance, jusqu'a zero. */
    ST_LINEAR,
    /** Diminue logarithmiquement et n'atteind jamais zero. */
    ST_LOGARITHM,
    /** Diminue logarithmiquement et atteind eventuellement zero. */
    ST_HYBRID
    };

    public:

    /** Voir doc Ogre. */
    void _notifyAttached(Ogre::Node* parent, bool isTagPoint = false);

    /** Voir doc Ogre. */
    const Ogre::String &getMovableType(void) const;

    /** Voir doc Ogre. */
    const Ogre::AxisAlignedBox &getBoundingBox(void) const;

    /** Voir doc Ogre. */
    Ogre::Real getBoundingRadius(void) const;

    /** Voir doc Ogre. */
    void _updateRenderQueue(Ogre::RenderQueue *);

    /** Joue le son. Si il était sur pause, il va jouer. */
    void play();

    /** Pause/dépause le son. */
    void setPaused(bool paused);

    /** Met le son en loop ou non. */
    void setLoop(bool loop);

    /**
    * Assigne la distance ou le son sera a plein volume (min) et la
    * distance ou le son sera inaudible (max). Entre min et max, le
    * volume va décroitre.
    */
    void setMinMaxDistance(float min, float max);

    /** Assigne la fréquence du son. */
    void setFrequency(float freq);

    /** Ajuste le volume du son. */
    void setVolume(float volume);

    /** Retourne si le son est pausé. */
    bool getPaused() const;

    /** Retourne si le son est en boucle. */
    bool getLooped() const;

    /** Retourne la fréquence du son. */
    float getFrequency() const;

    /** Retourne le volume. */
    float getVolume() const;

    private:

    /** Constructeur. Privé, on laisse le SoundManager faire le travail. */
    Sound();

    /** Constructeur de copie. */
    Sound(const Sound&);

    /** Destructeur. */
    ~Sound() {};

    private:

    SoundManager* m_sm;
    RolloffType m_rolloffType;

    FMOD::Channel* m_channel;
    FMOD_VECTOR* m_rolloff;

    static Ogre::String m_objType;
    mutable Ogre::AxisAlignedBox m_box;

    };

    /**
    * Gestionnaire de son qui encapsule FMOD de facon tres simple pour offrir
    * les fonctionnalité minimum requise pour positionner des sons dans une
    * scène.
    */
    class SIM_DLLEXPORT SoundManager : public Ogre::Singleton<SoundManager>,
    public Ogre::FrameListener
    {
    public:

    /** Mode automatique. */
    enum AutoMode
    {
    /** Pas automatique. */
    AM_NOT_AUTOMATIC,
    /** Les sons seront mis-a-jour automatiquement au debut du frame. */
    AM_AUTO_START,
    /** Les sons seront mis-a-jour automatiquement a la fin du frame. */
    AM_AUTO_END
    };

    public:

    /** Constructeur par defaut. */
    SoundManager();

    /** Initialize le gestionnaire. */
    bool initialize(Ogre::SceneManager* sm);

    /** Active/désactive le mode automatique. */
    void setAutomatic(AutoMode mode, Ogre::Root* root = 0);

    /** Assigne le noeud qui représente l'auditeur. */
    void setListenerNode(Ogre::SceneNode* node);

    /** Crée un nouveau son a partir d'un fichier. */
    Sound* createSound(const Ogre::String& filename,
    Sound::SoundType type = Sound::ST_3D,
    bool loop = false,
    Sound::RolloffType rolloff = Sound::ST_LINEAR);

    /** Met a jour le système de son. */
    void update(float timeStep);

    /** Pour frame listener en mode automatique. */
    bool frameStarted(const Ogre::FrameEvent &evt);

    /** Pour frame listener en mode automatique. */
    bool frameEnded(const Ogre::FrameEvent &evt);

    private:

    void update3DParameters(float step);
    void updateOcclusionParameters();

    private:

    typedef std::vector<Sound*> SoundList;

    private:

    bool m_listenerChanged;
    SoundList m_sounds;
    AutoMode m_autoMode;
    Ogre::Root* m_root;
    Ogre::SceneNode* m_listenerNode;
    Ogre::SceneManager* m_sceneManager;
    Ogre::Vector3 m_prevListenerPos;
    FMOD::System* m_system;

    };

    }

    #endif


    SoundManager.cpp


    #include "SimSoundManager.h"

    using namespace Sim;
    using namespace Ogre;

    #define NUMREALCHANNELS 10
    #define NUMCHANNELS 50

    template<> SoundManager* Singleton<SoundManager>::ms_Singleton = 0;
    String Sound::m_objType("Sound");

    SoundManager::SoundManager() :
    m_system(NULL),
    m_listenerNode(NULL),
    m_sceneManager(NULL),
    m_autoMode(AM_NOT_AUTOMATIC),
    m_listenerChanged(false)
    {
    }

    //-----------------------------------------------------------------------------
    bool SoundManager::initialize(SceneManager* sm)
    {
    FMOD_RESULT result;

    // On cree le systeme de son.
    result = FMOD::System_Create(&m_system);

    if (result != FMOD_OK)
    return false;

    result = m_system->setSoftwareChannels(NUMREALCHANNELS);

    //FMOD_CAPS caps;
    //FMOD_SPEAKERMODE speakermode;

    //result = m_system->getDriverCaps(0, &caps, NULL, NULL, &speakermode);
    //result = m_system->setSpeakerMode(speakermode);

    if (result != FMOD_OK)
    return false;

    result = m_system->init(NUMCHANNELS, FMOD_INIT_OCCLUSION_LOWPASS, 0);

    if (result != FMOD_OK)
    return false;

    m_sceneManager = sm;

    return true;
    }

    //-----------------------------------------------------------------------------
    void SoundManager::setAutomatic(AutoMode mode, Ogre::Root* root)
    {
    if (m_root && (mode == AM_AUTO_START || mode == AM_AUTO_END))
    {
    m_root->removeFrameListener(this);
    m_root = NULL;
    }

    m_autoMode = mode;

    if (root && (mode == AM_AUTO_START || mode == AM_AUTO_END))
    {
    m_root = root;
    m_root->addFrameListener(this);
    }
    }

    //-----------------------------------------------------------------------------
    Sound* SoundManager::createSound(const String& filename, Sound::SoundType type, bool loop, Sound::RolloffType rolloff)
    {
    // On utilise le resource manager pour trouver le fichier sonore.
    FileInfoListPtr list = ResourceGroupManager::getSingleton(
    ).findResourceFileInfo("Simulator", filename);

    if (list->size() != 1)
    return NULL;

    String fullpath = list->front().archive->getName() + "/" +
    list->front().filename;

    // On determine les flags de FMOD.
    FMOD_MODE mode = FMOD_SOFTWARE;

    switch (type)
    {
    case Sound::ST_2D : mode |= FMOD_2D; break; /* | FMOD_3D_LINEARROLLOFF */
    case Sound::ST_3D : mode |= FMOD_3D; break;
    default : return NULL;
    }

    if (loop)
    mode |= FMOD_LOOP_NORMAL;

    switch (rolloff)
    {
    case Sound::ST_LINEAR : mode |= FMOD_3D_LINEARROLLOFF; break;
    case Sound::ST_LOGARITHM : mode |= FMOD_3D_LOGROLLOFF; break;
    case Sound::ST_HYBRID : mode |= FMOD_3D_CUSTOMROLLOFF; break;
    }

    FMOD_RESULT result;

    FMOD::Sound* fmodsound = NULL;
    FMOD::Channel* fmodchannel = NULL;

    result = m_system->createSound(
    fullpath.c_str(),
    mode, 0, &fmodsound);

    if (result != FMOD_OK)
    return false;

    result = m_system->playSound(FMOD_CHANNEL_FREE,
    fmodsound, true, &fmodchannel);

    if (result != FMOD_OK)
    {
    delete fmodchannel;
    return false;
    }

    Sound* sound = new Sound();

    sound->m_channel = fmodchannel;
    sound->m_rolloffType = rolloff;

    // Valeurs par defaut.
    sound->setMinMaxDistance(10, 100);

    m_sounds.push_back(sound);

    return sound;
    }

    //-----------------------------------------------------------------------------
    void SoundManager::setListenerNode(SceneNode* node)
    {
    if (m_listenerNode != node)
    m_listenerChanged = true;

    m_listenerNode = node;
    }

    //-----------------------------------------------------------------------------
    void SoundManager::update(float timeStep)
    {
    if (!m_listenerNode)
    return;

    const Vector3 pos = m_listenerNode->getWorldPosition();
    const Vector3 vel = Vector3::ZERO;
    const Vector3 fwd = m_listenerNode->getWorldOrientation() * Vector3::UNIT_Z;
    const Vector3 up = m_listenerNode->getWorldOrientation() * Vector3::UNIT_Y;

    m_system->set3DListenerAttributes(
    0,
    (FMOD_VECTOR*)&pos,
    (FMOD_VECTOR*)&vel,
    (FMOD_VECTOR*)&fwd,
    (FMOD_VECTOR*)&up);

    // Calcul de la position et velocite
    update3DParameters(timeStep);

    // Calcul des parametres d'occlusion.
    updateOcclusionParameters();

    // Mise-a-jour de FMOD.
    m_system->update();

    m_listenerChanged = false;
    }

    //-----------------------------------------------------------------------------
    void SoundManager::update3DParameters(float step)
    {
    for (SoundList::iterator it = m_sounds.begin(); it != m_sounds.end(); it++)
    {
    Sound* sound = *it;
    SceneNode* node = sound->getParentSceneNode();

    if (node == NULL)
    continue;

    Vector3 prevPos;

    // On va chercher la position precedente.
    sound->m_channel->get3DAttributes((FMOD_VECTOR*)&prevPos, NULL);

    // On calcule la velocite.
    Vector3 pos = node->getWorldPosition();
    Vector3 vel = (step > 0) ? (pos - prevPos) / step : Vector3::ZERO;

    // On change les parametres.
    sound->m_channel->set3DAttributes((FMOD_VECTOR*)&pos,
    (FMOD_VECTOR*)&vel);
    }
    }

    //-----------------------------------------------------------------------------
    void SoundManager::updateOcclusionParameters()
    {
    Ray ray(m_listenerNode->getWorldPosition(), Vector3::UNIT_X);

    RaySceneQuery* query = m_sceneManager->createRayQuery(ray);

    query->setWorldFragmentType(SceneQuery::WFT_SINGLE_INTERSECTION);
    query->setQueryTypeMask(SceneManager::ENTITY_TYPE_MASK);

    for (SoundList::iterator it = m_sounds.begin(); it != m_sounds.end(); it++)
    {
    Sound* sound = *it;

    // Si le son n'est pas attache a un noeud, il n'a pas de position...
    if (sound->getParentSceneNode() == NULL)
    continue;

    bool virt;
    bool play;

    sound->m_channel->isVirtual(&virt);
    sound->m_channel->isPlaying(&play);

    // Les sons virtuels ne sont pas audible.
    // Si le son ne joue pas, evidemment, on s'en fou.
    if (virt || !play)
    continue;

    // On cree un rayon qui part de l'auditeur jusqu'au son.
    Vector3 line = sound->getParentNode()->getWorldPosition() - ray.getOrigin();

    ray.setDirection(line.normalisedCopy());

    query->setRay(ray);
    query->setSortByDistance(true, 0);

    // On execute une requete pour savoir si il y a une intersection.
    RaySceneQueryResult result = query->execute();

    // Si il y a une intersection, on va verifier si elle se produit AVANT le son.
    if (result.size() > 0)
    {
    bool found = false;

    // Le PLSM2 ne semble pas valider le type de movable object... on le fait a la main!
    for (RaySceneQueryResult::iterator qit = result.begin(); qit != result.end(); qit++)
    {
    if (qit->movable != NULL &&
    qit->movable->getTypeFlags() & SceneManager::ENTITY_TYPE_MASK &&
    qit->distance < line.length() &&
    !qit->movable->getWorldBoundingBox().intersects(ray.getOrigin()) &&
    !qit->movable->getWorldBoundingBox().intersects(sound->getParentSceneNode()->getWorldPosition()))
    {
    sound->m_channel->set3DOcclusion(0.5f, 0.5f);
    found = true;
    break;
    }
    }

    // Pas d'occlusion entre l'auditeur et le son.
    if (!found)
    sound->m_channel->set3DOcclusion(0.0f, 1.0f);
    }
    else
    {
    sound->m_channel->set3DOcclusion(0.0f, 1.0f);
    }
    }

    delete query;
    }

    //-----------------------------------------------------------------------------
    bool SoundManager::frameStarted(const FrameEvent &evt)
    {
    if (m_autoMode == AM_AUTO_START)
    update(evt.timeSinceLastFrame);

    return true;
    }

    //-----------------------------------------------------------------------------
    bool SoundManager::frameEnded(const FrameEvent &evt)
    {
    if (m_autoMode == AM_AUTO_END)
    update(evt.timeSinceLastFrame);

    return false;
    }

    //-----------------------------------------------------------------------------
    Sound::Sound() :
    m_rolloff(NULL),
    MovableObject("testchannel")
    {
    }

    //-----------------------------------------------------------------------------
    void Sound::_notifyAttached(Node* parent, bool isTagPoint)
    {
    MovableObject::_notifyAttached(parent, isTagPoint);
    }

    //-----------------------------------------------------------------------------
    const String &Sound::getMovableType(void) const
    {
    return m_objType;
    }

    //-----------------------------------------------------------------------------
    const AxisAlignedBox &Sound::getBoundingBox(void) const
    {
    /*
    float radius = getBoundingRadius();

    m_box = AxisAlignedBox(-radius, -radius, -radius,
    radius, radius, radius);

    return m_box;
    */

    //if (mParentNode)
    //{
    // const Vector3& pos = mParentNode->getPosition();
    // m_box = AxisAlignedBox(pos, pos);
    //}
    //else
    //{
    m_box.setNull();
    //}

    return m_box;
    }

    //-----------------------------------------------------------------------------
    Real Sound::getBoundingRadius(void) const
    {
    //float min;
    //float max;

    //m_channel->get3DMinMaxDistance(&min, &max);

    //return max;

    return 0;
    }

    //-----------------------------------------------------------------------------
    void Sound::_updateRenderQueue(RenderQueue *)
    {
    }

    //-----------------------------------------------------------------------------
    void Sound::play()
    {
    m_channel->setPaused(false);
    }

    //-----------------------------------------------------------------------------
    void Sound::setPaused(bool paused)
    {
    m_channel->setPaused(paused);
    }

    //-----------------------------------------------------------------------------
    void Sound::setLoop(bool loop)
    {
    }

    //-----------------------------------------------------------------------------
    void Sound::setMinMaxDistance(float min, float max)
    {
    if (m_rolloffType == ST_HYBRID)
    {
    if (m_rolloff)
    delete [] m_rolloff;

    m_rolloff = new FMOD_VECTOR[3];

    FMOD_VECTOR p1 = { 0, 1, 0 };
    FMOD_VECTOR p2 = { min, 1, 0 };
    FMOD_VECTOR p3 = { max, 0, 0 };

    m_rolloff[0] = p1;
    m_rolloff[1] = p2;
    m_rolloff[2] = p3;

    m_channel->set3DCustomRolloff(m_rolloff, 3);
    }
    else
    {
    m_channel->set3DMinMaxDistance(min, max);
    }
    }

    //-----------------------------------------------------------------------------
    void Sound::setFrequency(float freq)
    {
    m_channel->setFrequency(freq);
    }

    //-----------------------------------------------------------------------------
    void Sound::setVolume(float volume)
    {
    m_channel->setVolume(volume);
    }

    //-----------------------------------------------------------------------------
    bool Sound::getPaused() const
    {
    bool paused;
    m_channel->getPaused(&paused);
    return paused;
    }

    //-----------------------------------------------------------------------------
    bool Sound::getLooped() const
    {
    int loopcount;
    m_channel->getLoopCount(&loopcount);
    return loopcount == -1;
    }

    //-----------------------------------------------------------------------------
    float Sound::getFrequency() const
    {
    float freq;
    m_channel->getFrequency(&freq);
    return freq;
    }

    //-----------------------------------------------------------------------------
    float Sound::getVolume() const
    {
    float vol;
    m_channel->getVolume(&vol);
    return vol;
    }


    Mat

Bekas

20-04-2007 22:28:04

Well, an idea is to go with the C++/CLI route. You'd create a Mogre.MovableObject subclass that instantiates and wraps the native Sound class.
The problem is that due to a bad decision of mine, there are "internal" (visible only to the dll) variables/constructors, and that means that you can only do such a subclass by adding it to the Mogre project and recompile :?

I'm going to change all internal stuff to protected, so this stuff are possible by a separate C++/CLI DLL.

bleubleu

22-04-2007 02:36:49

That would be awesome.

Mat