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
- Sound are movable objects, so velocity and position can be computed at each time step. [/*:m]
- 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]
- 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