SoundManager的解析和在OGRE中使用fmod
【SoundManager的解析】
注意到了程序中使用的语句:
=================================
第一句:soundMgr=new SoundManager;
=================================
在SoundManager.h中定义了SoundManager类,他的构造函数
SoundManager::SoundManager()
{
system = NULL;
prevListenerPosition = Vector3(0, 0, 0);
soundInstanceVector = new SoundInstanceVector;
//typedef std::vector<SoundInstance *> SoundInstanceVector;
//这里定义了一个容器vector,装载指向我们的SoundInstance类的指针
nextSoundInstanceIndex = 0;//下标为0,指向容器的第一个元素
//通过递增下标简单初始化100个元素,用clear函数设置他们的各项属性为空
soundInstanceVector->resize(INITIAL_VECTOR_SIZE);//INITIAL_VECTOR_SIZE为100
for (int vectorIndex = 0; vectorIndex < INITIAL_VECTOR_SIZE; vectorIndex++)
{
soundInstanceVector->at(vectorIndex) = new SoundInstance;
soundInstanceVector->at(vectorIndex)->Clear();
}
//通过递增下标简单初始化200个元素,用clear函数设置他们的各项属性为空
for (int channelIndex = 0; channelIndex < MAX_SOUND_CHANNELS; channelIndex++)
channelArray[channelIndex].Clear();
}
这里利用了STL的容器,这样相对于一般的数组或者链表,string , vector等的at()成员函数相较下标运算符[]而言,增加了下标越界检查、异常处理等。增加程序的安全性和稳定性。这一句主要实现了构架封装,与fmod本身关联不大。
==============================
第二句:soundMgr->Initialize()
==============================
void SoundManager::Initialize(void)
{
FMOD_RESULT result; //fmod函数执行结果的判断依据,执行结果的查询
//建立一个FMOD::System ,然后检查建立的结果
result = FMOD::System_Create(&system);
if (result != FMOD_OK)
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");
//系统初始化,参数1:代表能播放音乐的最大channel数量
//参数2:初始化类型设定,可以使用|符号来制定多种规格的合并
//参数3:额外数据,可以传递给外部输出插件,比如写出文件名,这里我们不用
result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0); // Initialize FMOD.
if (result != FMOD_OK)
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");
//设置3D效果,参数1:多普勒效应的变换因素。想象下汽车快速从我们身边开过,靠近我们的时候喇叭变得尖锐
//远离的时候变得低沉的效果。这个因素就是这种效果的剧烈程度,越大越明显
//参数2:距离比例,假设为X,那么程序中的X米=现实中的1米.
//参数3:高频音效衰减?跟声音类型有关?不太理解,默认为1
system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);
//有些旧版本的>setFileSystem只有5个参数,新版本是7个参数,那两个为0的参数是
// FMOD_FILE_ASYNCREADCALLBACK userasyncread, 异步读,
// FMOD_FILE_ASYNCCANCELCALLBACK userasynccancel, 异步取消
// 不太了解具体的作用,看函数名来理解吧
result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback, &fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048);
if (result != FMOD_OK)
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");
Ogre::LogManager::getSingleton().logMessage("SoundManager Initialized");
}
抽出来呢,Initial就下面几句话:
FMOD::System system;
result = FMOD::System_Create(&system);
result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0);
system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);
result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback,&fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048)//这个函数到现在还不是特别理解,就知道是设置了4个函数,供打开、关闭、阅读、搜索文件调用
======================================================================
第三句:
qplSoundIndex=soundMgr->CreateLoopedSound(Ogre::String("qpl.wma"));
======================================================================
int SoundManager::CreateLoopedSound(String &fileName)
{
return CreateSound(fileName, SOUND_TYPE_3D_SOUND_LOOPED);
}
CreateLoopedSound调用了CreateSound(fileName, SOUND_TYPE_3D_SOUND),那么转去看CreateSound的代码
int SoundManager::CreateSound(String &fileName, SOUND_TYPE soundType)
{
Archive * fileArchive;
FMOD_RESULT result;
FMOD::Sound * sound;
String fullPathName;
SoundInstance *newSoundInstance;
int soundIndex;
soundIndex = FindSound(fileName, soundType); //查看容器中是否已经有这个音乐文件
if (soundIndex != INVALID_SOUND_INDEX)
return soundIndex; //如果已经有了,返回他的下标
fullPathName = fileName;
FileLocator * fileLocator = (FileLocator * )ResourceGroupManager::getSingletonPtr();
fileArchive = fileLocator->Find(fullPathName); //在资源路径FileSystem中查找这个文件明
if (!fileArchive) //找不到
{
Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not find sound '" + fileName + "'");
return INVALID_SOUND_INDEX;
}
IncrementNextSoundInstanceIndex(); //声音实例容器下标+1
newSoundInstance = soundInstanceVector->at(nextSoundInstanceIndex);//容器新增的元素
newSoundInstance->fileName = fileName;
newSoundInstance->fileArchive = fileArchive;
newSoundInstance->soundType = soundType;
switch (soundType) //根据指定的soundType声音类型来建立声音,刚才我们默认使用SOUND_TYPE_3D_SOUND_LOOPED类型
{
case SOUND_TYPE_3D_SOUND:
{
result = system->createSound((const char *)newSoundInstance, FMOD_3D, 0, &sound);
break;
}
case SOUND_TYPE_3D_SOUND_LOOPED:
{
//这里才是真正的创建声音 system->createSound()
result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
break;
}
case SOUND_TYPE_2D_SOUND:
{
result = system->createStream((const char *)newSoundInstance, FMOD_DEFAULT, 0, &sound);
break;
}
case SOUND_TYPE_2D_SOUND_LOOPED:
{
result = system->createStream((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_2D | FMOD_HARDWARE, 0, &sound);
break;
}
default:
{
Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not load sound '" + fileName + "' (invalid soundType)");
return INVALID_SOUND_INDEX;
}
}
if (result != FMOD_OK)
{
Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not load sound '" + fileName + "' FMOD Error:" + FMOD_ErrorString(result));
return INVALID_SOUND_INDEX;
}
newSoundInstance->fmodSound = sound;
return nextSoundInstanceIndex; //返回该声音实例的下标
}
抽出来就这一句:
result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
其他大部分时间都在操作声音实例的容器,添加,查找,赋值等等。
FMOD::system->createSound();函数原型为:
FMOD_RESULT System::createSound(
const char * name_or_data,
FMOD_MODE mode,
FMOD_CREATESOUNDEXINFO * exinfo,
FMOD::Sound ** sound
)
参数1表示要创建的声音文件的名字,可以带路径,比如:“../media/qpl.wma”
参数2表示设置声音效果,支持用 | 来实现多种效果合并
参数3额外信息,比如用户播放音乐时音乐的长度,这里不用设为0
参数4将新创建的音乐地址赋值给sound,可以使用sound来操作该音乐的播放了
=================================================================
第四、五句:
qplSoundChannel=INVALID_SOUND_CHANNEL; //播放频道下标,初始值为-1
soundMgr->PlaySound(qplSoundIndex,soundNode,&qplSoundChannel);
=================================================================
第四句不管他,主要是看第五句PlaySound()函数
void SoundManager::PlaySound(int soundIndex, SceneNode *soundNode, int *channelIndex)
{
int channelIndexTemp;
FMOD_RESULT result;
FMOD_VECTOR initialPosition;
FMOD::Channel *channel;
SoundInstance *soundInstance; //各种初始化,看看就好了
if (soundIndex == INVALID_SOUND_INDEX) //无效音乐实例下标
return;
if (channelIndex)
channelIndexTemp = *channelIndex;
else
channelIndexTemp = INVALID_SOUND_CHANNEL;
assert((soundIndex > 0) && (soundIndex < (int)soundInstanceVector->capacity()));//断言,基本是供调试用
// 如果channel下标所指向的channel正在播放声音,检查与PlaySound函数给定的ScenNode是否一致
//如果检测到该Node已经在播放指定声音了,那么返回,不再重复播放
if ((channelIndexTemp != INVALID_SOUND_CHANNEL) && (channelArray[channelIndexTemp].sceneNode != NULL))
{
result = system->getChannel(channelIndexTemp, &channel);
if (result == FMOD_OK)
{
bool isPlaying;
result = channel->isPlaying(&isPlaying);
if ((result == FMOD_OK) && (isPlaying == true) && (channelArray[channelIndexTemp].sceneNode == soundNode))
return; // 已经在该Node播放了该声音
}
}
//否则准备播放
soundInstance = soundInstanceVector->at(soundIndex);//根据指定下标取实例
//播放FMOD::System::playSound
//参数1表示使用哪个channel播放,FMOD_CHANNEL_FREE,表示使用当前空闲通道来播放
//参数2表示要播放的声音
//参数3表示 paused,true表示在发出声音之前允许用户修改声音属性,如set3DAttributes;False的话直接在这里就发出声音了
//参数4让如果成功播放声音,channel将指向播放这个声音的频道
result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
if (result != FMOD_OK)
{
Ogre::LogManager::getSingleton().logMessage(String("SoundManager::PlaySound could not play sound FMOD Error:") + FMOD_ErrorString(result));
if (channelIndex)
*channelIndex = INVALID_SOUND_CHANNEL;//播放完了之后channel又赋值-1,空闲准备下一个音乐的播放
return;
}
//保存该播放声音的channel所绑定到的SceneNode
channel->getIndex(&channelIndexTemp);
channelArray[channelIndexTemp].sceneNode = soundNode;
if (soundNode)
{
channelArray[channelIndexTemp].prevPosition = soundNode->getPosition();
initialPosition.x = soundNode->getPosition().x;
initialPosition.y = soundNode->getPosition().y;
initialPosition.z = soundNode->getPosition().z;
channel->set3DAttributes(&initialPosition, NULL);//设置3D属性,播放位置,运动速度
}
result = channel->setVolume(1.0);//音量大小
// paused=false,表示播放,也就是开始发出声音
result = channel->setPaused(false);
if (channelIndex)
*channelIndex = channelIndexTemp;
}
主要是检测是否正在播放,再就是保存Node,一些繁杂的数组和容器操作。
同样,为了学习,把Fmod函数部分抽出来:
result = system->getChannel(channelIndexTemp, &channel);
bool isPlaying;
result = channel->isPlaying(&isPlaying);
result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
initialPosition.x = soundNode->getPosition().x;
initialPosition.y = soundNode->getPosition().y;
initialPosition.z = soundNode->getPosition().z; //取Node坐标
channel->set3DAttributes(&initialPosition, NULL); //设置3D属性,播放位置,运动速度
result = channel->setVolume(1.0); //音量大小
result = channel->setPaused(false);
========================================================
第六句:
soundMgr->FrameStarted(mCamNode,evt.timeSinceLastFrame);
========================================================
void SoundManager::FrameStarted(Ogre::SceneNode *listenerNode, Ogre::Real timeElapsed)
{
int channelIndex;
FMOD::Channel *nextChannel;
FMOD_VECTOR listenerPosition;
FMOD_VECTOR listenerForward;
FMOD_VECTOR listenerUp;
FMOD_VECTOR listenerVelocity;
Ogre::Vector3 vectorVelocity;
Ogre::Vector3 vectorForward;
Ogre::Vector3 vectorUp; //各种定义,看看就好
//下面的代码是检测【听众】的移动方位、速度来设置3D音效,如多普勒效应和立体声音
// 速度=路程 / 时间
if (timeElapsed > 0)
vectorVelocity = (listenerNode->getPosition() - prevListenerPosition) / timeElapsed;
else
vectorVelocity = Vector3(0, 0, 0);
//Forward 和 up
vectorForward = listenerNode->getOrientation().zAxis();
vectorForward.normalise();
vectorUp = listenerNode->getOrientation().yAxis();
vectorUp.normalise();
//取得听众位置
listenerPosition.x = listenerNode->getPosition() .x;
listenerPosition.y = listenerNode->getPosition() .y;
listenerPosition.z = listenerNode->getPosition() .z;
//设置听众的Forward 、up属性,下面的函数需要提供这些参数
listenerForward.x = vectorForward.x;
listenerForward.y = vectorForward.y;
listenerForward.z = vectorForward.z;
listenerUp.x = vectorUp.x;
listenerUp.y = vectorUp.y;
listenerUp.z = vectorUp.z;
//设置听众速度
listenerVelocity.x = vectorVelocity.x;
listenerVelocity.y = vectorVelocity.y;
listenerVelocity.z = vectorVelocity.z;
// 设置听众的3D属性,保存当前位置,一边下一帧进行Pre的计算
system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
system->update();
prevListenerPosition = listenerNode->getPosition() ;
//下面的代码是检测【声源】的移动来设置3D音效,如多普勒效应
//用for循环取得所有绑定到节点Node的声音,计算他们的移动速度
for (channelIndex = 0; channelIndex < MAX_SOUND_CHANNELS; channelIndex++)
{
if (channelArray[channelIndex].sceneNode != NULL)
{
system->getChannel(channelIndex, &nextChannel);
if (timeElapsed > 0)
vectorVelocity = (channelArray[channelIndex].sceneNode->getPosition() - channelArray[channelIndex].prevPosition) / timeElapsed;
else
vectorVelocity = Vector3(0, 0, 0);
//取得听众位置
listenerPosition.x = channelArray[channelIndex].sceneNode->getPosition().x;
listenerPosition.y = channelArray[channelIndex].sceneNode->getPosition().y;
listenerPosition.z = channelArray[channelIndex].sceneNode->getPosition().z;
//听众移动速度(即使是听众不动,但是Node移动,这个速度是指相对速度)
listenerVelocity.x = vectorVelocity.x;
listenerVelocity.y = vectorVelocity.y;
listenerVelocity.z = vectorVelocity.z;
//设置播放声音的channel的3D属性,然后同样是保存Node位置方便下一帧查询
nextChannel->set3DAttributes(&listenerPosition, &listenerVelocity);
channelArray[channelIndex].prevPosition = channelArray[channelIndex].sceneNode->getPosition();
}
}
}
FrameStarted主要是实现了3D场景的声音属性,比如立体声音,假设是声源在你右边,右耳朵听起来会比较大声,还有就是多普勒效应了,当你面向声源移动(或者声源面向你移动)声音会变得比较尖锐;反之低沉。注意的是,fmod声音引擎有自己的坐标系,FMOD_VECTOR posFmod;Ogre也有自己的坐标系 Ogre::Vector3 posOgre,我们用Ogre的距离表现声音距离的时候应该注意转换:
posFmod.x = posOgre.x;
posFmod.y = posOgre.y;
posFmod.z = posOgre.z;
惯例,抽取Fmod的函数代码:
//set3DListenerAttributes函数,system->update每帧执行更新
//参数1听众编号,只有一个的话设为0
//参数2听众位置,影响音量大小
//参数3听众速度,影响多普勒效应
//参数4听众面向,影响左右耳声音大小,3D场景模拟现实判断声源位置的依据
//参数5听众的向上位置?必须与面向垂直,楼主也不知道做什么用
system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
system->update();
//set3DAttributes
//参数1听众位置,影响音量大小
//参数2和听众的相对速度,影响多普勒效应
Channel->set3DAttributes(&listenerPosition, &listenerVelocity);
=============================================================
第七句:
soundMgr->Set3DMinMaxDistance(qplSoundChannel,50.0f,2000.0f);
=============================================================
//设置距离对声音的影响
//0~minDistance, 声音为最大且不变;minDistance~maxDistance,声音线性减小到预设的最小值
//maxDistance~∞ ,声音为预设的最小值,且不变
void SoundManager::Set3DMinMaxDistance(int channelIndex, float minDistance, float maxDistance)
{
FMOD_RESULT result;
FMOD::Channel *channel;
if (channelIndex == INVALID_SOUND_CHANNEL)
return;
result = system->getChannel(channelIndex, &channel);
if (result == FMOD_OK)
channel->set3DMinMaxDistance(minDistance, maxDistance);
}
抽取fmod函数:
channel->set3DMinMaxDistance(minDistance, maxDistance);
================================================
好了现在已经把SoundManager的主心骨都给抽出来了
================================================
//最基本的播放实现
FMOD::System system;
result = FMOD::System_Create(&system);
result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0);
system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);
result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback, &fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048)
result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
channel->set3DMinMaxDistance(100.0f, 2000.0f);
result = channel->setPaused(false);
//为了实现3D音效,而增加的代码
initialPosition.x = soundNode->getPosition().x;
initialPosition.y = soundNode->getPosition().y;
initialPosition.z = soundNode->getPosition().z; //取Node坐标
channel->set3DAttributes(&initialPosition, NULL); //设置3D属性,播放位置,运动速度
result = channel->setVolume(1.0); //音量大小
result = channel->setPaused(false);
//下面的代码最好放在帧循环中随时更新
system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
system->update();
Channel->set3DAttributes(&listenerPosition, &listenerVelocity);
【在OGRE中使用fmod】
=================================================
现在试试不用SoundManager,试试我们的Ogre播放声音。
=================================================
1.在昨天使用了SoundManager的基础上,重新Load【最简单的缓冲输入实现】的代码,这样没有包含SoundManager,确保昨天的能正确运行并播放声音哦,否则这一章还是要用了一些库文件,和资源配置,不会的话参考上一篇文章,只是不要在 OgreTemplate.cpp中包含SoundManager.的文件头和cpp)
然后在OgreTemplate.cpp中最前面加上句 :#include "fmod.hpp" 和 #include "fmod_errors.h”,因为这两个包含原本是在SoundManager里的。
2.定义一些全局变量(为了便于学习理解,省去参数传递的麻烦,但编写要发行的游戏该视具体情况而定)
SceneNode *mCamNode;
SceneNode *soundNode;
FMOD::System soundsystem; //最好还是不要用系统的一些名称如system,这样会有安全隐患
FMOD::Sound sound;
FMOD::Channel channel;
FMOD_RESULT result;
3.Application类的 createScene函数返回之前添加下面代码,本来每执行一条就应该检查result的建立结果的,为了篇幅我省掉了,要是编写发行版的游戏,应该检查每次result结果是否等于 FMOD_OK,调试程序运行不正确的话我们也可以这样一个个来检查哪一步出了问题:
FMOD::System_Create(&soundsystem);
result = soundsystem->init(50, FMOD_INIT_NORMAL, 0);
//因为程序相机移动速度太快,每秒上百米多普勒效应太明显,设置成0.1来减少
result = soundsystem->set3DSettings(0.1f, 1.0, 1.0);
//创建声音
result = soundsystem->createSound("../../media/sound/qpl.wma", FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
//播放声音。参数3表示允许在发出声音之前设置声音参数,设置完了之后记得关掉paused
result = soundsystem->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
//0~100声音最大,100~2000为衰减距离,2000以上声音不再减小
channel->set3DMinMaxDistance(100.0f, 2000.0f);
//关掉paused,开始发出声音
result = channel->setPaused(false);
这时候应该能播放音乐了,如果播放不了,请检查声音源文件的位置,或者直接把声音文件qpl.wma放到.exe的文件夹中"../../media/sound/qpl.wma"改成 "qpl.wma"表示在当前目录下寻找名为qpl.wma的声音文件。
4.我们设计一个函数soundFrameStart()来实现简单的3D音效效果
在myFrameListener类public中添加这个函数,然后在frameStarted()函数中,每一帧都执行它进行更新
void soundFrameStart(Real timeElapsed)
{
FMOD_VECTOR listenerPosition;
FMOD_VECTOR listenerForward;
FMOD_VECTOR listenerUp;
FMOD_VECTOR listenerVelocity;
Ogre::Vector3 vectorVelocity;
Ogre::Vector3 vectorForward;
Ogre::Vector3 vectorUp;
if (timeElapsed > 0)
vectorVelocity = (mCamNode->getPosition() - prevListenerPosition) / timeElapsed;
else
vectorVelocity = Vector3(0, 0, 0);
vectorForward = mCamNode->getOrientation().zAxis();
vectorForward.normalise();
vectorUp = mCamNode->getOrientation().yAxis();
vectorUp.normalise();
listenerForward.x = vectorForward.x;
listenerForward.y = vectorForward.y;
listenerForward.z = vectorForward.z;
listenerUp.x = vectorUp.x;
listenerUp.y = vectorUp.y;
listenerUp.z = vectorUp.z;
listenerPosition.x = mCamNode->getPosition() .x;
listenerPosition.y = mCamNode->getPosition() .y;
listenerPosition.z = mCamNode->getPosition() .z;
listenerVelocity.x = vectorVelocity.x;
listenerVelocity.y = vectorVelocity.y;
listenerVelocity.z = vectorVelocity.z;
soundsystem->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
prevListenerPosition = mCamNode->getPosition() ;
soundsystem->update();
}
5.在frameStarted()函数中,return语句之前,添加如下代码:
soundFrameStart(evt.timeSinceLastFrame);
现在运行程序对着怪物头一动是不是声音会变尖锐和音量变大了呢?
把头偏向一边(相机视角),声源在视角的左边或者右边看能辨别音源位置不?
这里只实现了一个声音的效果,要是有多个声音,你还得建立更多的sound和channel,或者使用SoundManager来管理。Fmod还提供了很多功能参数来给我们使用,这个例子中用到的函数只是其中很小一部分,要进一步深入学习只能靠自己多看代码和例子了,
楼主水平有限,只能为大家入门抛砖引玉。
最后把代码贴出来
【附录1:】
OgreTemplate.cpp
#include "ExampleApplication.h"
#include "fmod.hpp"
#include "fmod_errors.h"
SceneNode *mCamNode;
SceneNode *soundNode;
FMOD::System *soundsystem;
FMOD::Channel *channel;
FMOD::Sound *sound;
FMOD_RESULT result;
Ogre::Vector3 prevListenerPosition;
class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
{
public:
TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager*sceneMgr)
: ExampleFrameListener(win, cam, true, true)
{
mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);
mDirection = Vector3::ZERO;
mSceneMgr = sceneMgr;
mRotate = 0.13;
mMove = 250;
mContinue = true;
}
protected:
Real mRotate;
Real mMove;
SceneManager *mSceneMgr;
bool mContinue;
Vector3 mDirection;
bool frameStarted(const FrameEvent &evt)
{
if(mMouse)
mMouse->capture();
if(mKeyboard)
mKeyboard->capture();
mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
soundFrameStart(evt.timeSinceLastFrame);
return mContinue;
}
void soundFrameStart(Real timeElapsed)
{
FMOD_VECTOR listenerPosition;
FMOD_VECTOR listenerForward;
FMOD_VECTOR listenerUp;
FMOD_VECTOR listenerVelocity;
Ogre::Vector3 vectorVelocity;
Ogre::Vector3 vectorForward;
Ogre::Vector3 vectorUp;
if (timeElapsed > 0)
{
vectorVelocity = (mCamNode->getPosition() - prevListenerPosition) / timeElapsed;
vectorVelocity/=5;
}
else
vectorVelocity = Vector3(0, 0, 0);
vectorForward = mCamNode->getOrientation().zAxis();
vectorForward.normalise();
vectorUp = mCamNode->getOrientation().yAxis();
vectorUp.normalise();
listenerForward.x = vectorForward.x;
listenerForward.y = vectorForward.y;
listenerForward.z = vectorForward.z;
listenerUp.x = vectorUp.x;
listenerUp.y = vectorUp.y;
listenerUp.z = vectorUp.z;
listenerPosition.x = mCamNode->getPosition() .x;
listenerPosition.y = mCamNode->getPosition() .y;
listenerPosition.z = mCamNode->getPosition() .z;
listenerVelocity.x = vectorVelocity.x;
listenerVelocity.y = vectorVelocity.y;
listenerVelocity.z = vectorVelocity.z;
soundsystem->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
prevListenerPosition = mCamNode->getPosition() ;
soundsystem->update();
}
bool mouseMoved(const OIS::MouseEvent &e)
{
if (e.state.buttonDown(OIS::MB_Right))
{
mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);
mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);
}
return true;
}
bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
bool keyPressed(const OIS::KeyEvent &e)
{
switch (e.key)
{
case OIS::KC_ESCAPE: mContinue = false; break;
case OIS::KC_UP:
case OIS::KC_W: mDirection.z -= mMove; break;
case OIS::KC_DOWN:
case OIS::KC_S: mDirection.z += mMove; break;
case OIS::KC_LEFT:
case OIS::KC_A: mDirection.x -= mMove; break;
case OIS::KC_RIGHT:
case OIS::KC_D: mDirection.x += mMove; break;
default: break;
}
return true;
}
bool keyReleased(const OIS::KeyEvent &e)
{ switch (e.key)
{
case OIS::KC_UP:
case OIS::KC_W: mDirection.z += mMove; break;
case OIS::KC_DOWN:
case OIS::KC_S: mDirection.z -= mMove; break;
case OIS::KC_LEFT:
case OIS::KC_A: mDirection.x += mMove; break;
case OIS::KC_RIGHT:
case OIS::KC_D: mDirection.x -= mMove; break;
default: break;
}
return true;
}
};
class TutorialApplication : public ExampleApplication
{
public:
void createCamera(void)
{
mCamera = mSceneMgr->createCamera("PlayerCam");
mCamera->setNearClipDistance(5);
}
void createScene(void)
{
mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));
// add a MESH
Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
soundNode = mSceneMgr->getRootSceneNode()->createChildSceneNode ("headNode");
soundNode->translate(Vector3(0,20,0));
soundNode->attachObject(ent);
// create the light
Light *light = mSceneMgr->createLight("Light1");
light->setType(Light::LT_POINT);
light->setPosition(Vector3(250, 150, 250));
// Create the scene node
mCamNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode");
mCamNode->translate(Vector3(-400, 200, 400));
mCamNode->yaw(Degree(-45));
mCamNode->attachObject(mCamera);
//add ground plane
Plane plane;
plane.normal=Vector3::UNIT_Y;
plane.d=0;
MeshManager::getSingleton().createPlane("floor",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,plane,4000,\
4000,10,10,true,1,50,50,Vector3::UNIT_Z);
Entity *pPlaneEnt=mSceneMgr->createEntity("grassfloor","floor");
pPlaneEnt->setMaterialName("Examples/GrassFloor");
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(pPlaneEnt);
//add sound
FMOD::System_Create(&soundsystem);
result = soundsystem->init(50, FMOD_INIT_NORMAL, 0);
result = soundsystem->set3DSettings(0.1f, 1.0, 1.0);
result = soundsystem->createSound("qpl.wma", FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
result = soundsystem->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
channel->set3DMinMaxDistance(100.0f, 2000.0f);
result = channel->setPaused(false);
}
void createFrameListener(void)
{
mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
mRoot->addFrameListener(mFrameListener);
mFrameListener->showDebugOverlay(true);
}
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
TutorialApplication app;
try {
app.go();
} catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occurred: %s\n",
e.getFullDescription().c_str());
#endif
}
return 0;
}