您的位置:首页 > 移动开发 > Objective-C

关于Ogre自带输入系统OIS(Object-Oriented Input System)的源码分析

2012-03-25 18:08 441 查看
在看Ogre源码的时候注意到了这个开源的输入系统OIS,全称是Object-Oriented Input System。

好像我看到有人称为 Open Input System,不管叫什么总之还挺好用的。

OIS应该有其他平台的版本,但目前我只在Win32需要,所以其他平台的OIS版本不做讨论。

我使用的OIS版本是1.2.0, 现在应该已经有新的版本了,不过应该变化不会太大。

注意:

1. 我们此次的讨论和Ogre无任何关系,完全是直接对OIS的使用。

2. 关于OIS的手柄和力反馈部分不做讨论,有兴趣我之后会研究下。

OK! 让我们开始。。。

首先让我们看下OIS的整体目录结构



Extras部分不做讨论,貌似是支持了其他手柄,比如wii

前面说过OIS是区分平台的,所以可以看到目录结构中有个Win32的文件夹,下面放得就是Win32下使用的源代码。

而直接在Header Files下面的头文件和源文件均为平台通用的接口层,具体实现层就在类似Win32,Linux等文件夹下(我猜的,因为我没下载其他平台的版本)

下看下各个类之间的关系图



如上图, 可以看到所有Mouse,Keyboard,JoyStick都是继承自Object,具体平台的实现层其实是前面带Win32的,而接口正如前面说的是中间那3个类。



如上图,OIS的对输入的管理无疑是归InputManager管理了。

接下来看源代码,先从管理类InputManager开始

InputManager的类中有四个重要的函数,如下:

static InputManager* createInputSystem( ParamList ¶mList );

static void destroyInputSystem(InputManager* manager);

Object* createInputObject( Type iType, bool bufferMode, const std::string &vendor = "");

void destroyInputObject( Object* obj );

前两个函数是用来创建和销毁OIS整个系统的,OIS在Win32上使用的是DX的DirectInput,具体DirectInput的使用方法在此不做讨论。

后两个函数是用来创建和销毁对象的(鼠标,键盘,手柄)。

InputManager.cpp
InputManager* InputManager::createInputSystem( ParamList ¶mList )
{
InputManager* im = 0;

#if defined OIS_SDL_PLATFORM
im = new SDLInputManager();
#elif defined OIS_WIN32_PLATFORM
im = new Win32InputManager();
#elif defined OIS_XBOX_PLATFORM
im = new XBoxInputManager();
#elif defined OIS_LINUX_PLATFORM
im = new LinuxInputManager();
#elif defined OIS_APPLE_PLATFORM
im = new MacInputManager();
#else
OIS_EXCEPT(E_General, "No platform library.. check build platform defines!");
#endif

try
{
im->_initialize(paramList);
}
catch(...)
{
delete im;
throw; //rethrow
}

return im;
}


看上面的代码可以发现针对不通的平台会有不通的InputManager创建,Win32下自然就是Win32InputManager了。

他调用了Win32InputManager下的初始化函数。

Win32InputManager.cpp
void Win32InputManager::_initialize( ParamList ¶mList )
{
HINSTANCE hInst = 0;
HRESULT hr;

//TODO 64 bit proof this little conversion xxx wip
//First of all, get the Windows Handle and Instance
ParamList::iterator i = paramList.find("WINDOW");
if( i == paramList.end() )
OIS_EXCEPT( E_InvalidParam, "Win32InputManager::Win32InputManager >> No HWND found!" );

hWnd  = (HWND)strtoul(i->second.c_str(), 0, 10);

if( IsWindow(hWnd) == 0 )
OIS_EXCEPT( E_General, "Win32InputManager::Win32InputManager >> The sent HWND is not valid!");

hInst = GetModuleHandle(0);

//Create the device
hr = DirectInput8Create( hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&mDirectInput, NULL );
if (FAILED(hr))
OIS_EXCEPT( E_General, "Win32InputManager::Win32InputManager >> Not able to init DirectX8 Input!");

//Ok, now we have DirectInput, parse whatever extra settings were sent to us
_parseConfigSettings( paramList );
_enumerateDevices();
}
看代码可以发现,是对DirectInput8的创建,然后设置了参数,然后去枚举设备了。

InputManager.cpp
Object* InputManager::createInputObject( Type iType, bool bufferMode, const std::string &vendor )
{
Object* obj = 0;
FactoryList::iterator i = mFactories.begin(), e = mFactories.end();
for( ; i != e; ++i)
{
if( (*i)->freeDevices(iType) > 0 )
{
if( vendor == "" || (*i)->vendorExist(iType, vendor) )
{
obj = (*i)->createObject(this, iType, bufferMode, vendor);
mFactoryObjects[obj] = (*i);
break;
}
}
}

if(!obj)
OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");

try
{	//Intialize device
obj->_initialize();
}
catch(...)
{	//Somekind of error, cleanup and rethrow
destroyInputObject(obj);
throw;
}

return obj;
}
从上面的代码可以看到其实没做什么,只是调用了createObject去创建Object,然后就初始化_initialize去了。

这两个函数很自然的想到是和平台相关的,具体的实现自然要到平台相关的InputManager中要,

Win32InputManager.cpp
Object* Win32InputManager::createObject(InputManager* creator, Type iType, bool bufferMode, const std::string & vendor)
{
Object *obj = 0;

switch(iType)
{
case OISKeyboard:
{
if( keyboardUsed == false )
obj = new Win32Keyboard(this, mDirectInput, bufferMode, kbSettings);
break;
}
case OISMouse:
{
if( mouseUsed == false )
obj = new Win32Mouse(this, mDirectInput, bufferMode, mouseSettings);
break;
}
case OISJoyStick:
{
for(JoyStickInfoList::iterator i = unusedJoyStickList.begin(); i != unusedJoyStickList.end(); ++i)
{
if(vendor == "" || i->vendor == vendor)
{
obj = new Win32JoyStick(this, mDirectInput, bufferMode, joySettings, *i);
unusedJoyStickList.erase(i);
break;
}
}
break;
}
default:
break;
}

if( obj == 0 )
OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");

return obj;
}
到这就可以看到具体创建不通的设备的代码了。

如上就完成了对OIS以及各个设备的创建,关于销毁的部分就非常简单了,就不讨论了。

下面看下具体设备的代码。

先看下各个设备的父类也就是Object类,他里面的东西都是各个设备的通用属性,比如类型,名字,是否是缓冲模式等,有些函数是接口,需要各个设备自己实现。

接下来看看Mouse类和Win32Mouse类,根据上面InputManager和Win32InputManager的理解,这两个类肯定也是一个是对外的接口类,一个是具体实现类。

Mouse类很简单,三个函数

virtual void setEventCallback( MouseListener *mouseListener ) {mListener = mouseListener;}

MouseListener* getEventCallback() {return mListener;}

const MouseState& getMouseState() const { return mState; }

设置对鼠标的监听类,获得监听类指针以及获得鼠标的状态。

WIn32Mouse类同样简单,四个主要函数:

virtual void setBuffered(bool buffered); // 设置缓冲或者非缓冲模式

virtual void capture(); // 捕获鼠标,需要反复地调用

virtual void _initialize(); // 对鼠标的创建,看代码可以看到DirectInput的创建函数

bool _doMouseClick( int mouseButton, DIDEVICEOBJECTDATA& di ); // 在缓冲模式下有效的函数,用来调用已经注册的监听者

Win32Mouse.cpp
void Win32Mouse::capture()
{
//Clear old relative values
mState.X.rel = mState.Y.rel = mState.Z.rel = 0;

DIDEVICEOBJECTDATA diBuff[MOUSE_DX_BUFFERSIZE];
DWORD entries = MOUSE_DX_BUFFERSIZE;

HRESULT hr = mMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );
if( hr != DI_OK )
{
hr = mMouse->Acquire();
while( hr == DIERR_INPUTLOST )
hr = mMouse->Acquire();

hr = mMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );

//Perhaps the user just tabbed away, and coop settings
//are nonexclusive..so just ignore
if( FAILED(hr) )
return;
}

bool axesMoved = false;
//Accumulate all axis movements for one axesMove message..
//Buttons are fired off as they are found
for(unsigned int i = 0; i < entries; ++i )
{
switch( diBuff[i].dwOfs )
{
case DIMOFS_BUTTON0:
if(!_doMouseClick(0, diBuff[i])) return;
break;
case DIMOFS_BUTTON1:
if(!_doMouseClick(1, diBuff[i])) return;
break;
case DIMOFS_BUTTON2:
if(!_doMouseClick(2, diBuff[i])) return;
break;
case DIMOFS_BUTTON3:
if(!_doMouseClick(3, diBuff[i])) return;
break;
case DIMOFS_BUTTON4:
if(!_doMouseClick(4, diBuff[i])) return;
break;
case DIMOFS_BUTTON5:
if(!_doMouseClick(5, diBuff[i])) return;
break;
case DIMOFS_BUTTON6:
if(!_doMouseClick(6, diBuff[i])) return;
break;
case DIMOFS_BUTTON7:
if(!_doMouseClick(7, diBuff[i])) return;
break;
case DIMOFS_X:
mState.X.rel += diBuff[i].dwData;
axesMoved = true;
break;
case DIMOFS_Y:
mState.Y.rel += diBuff[i].dwData;
axesMoved = true;
break;
case DIMOFS_Z:
mState.Z.rel += diBuff[i].dwData;
axesMoved = true;
break;
default: break;
} //end switch
}//end for

if( axesMoved )
{
if( coopSetting & DISCL_NONEXCLUSIVE )
{
//DirectInput provides us with meaningless values, so correct that
POINT point;
GetCursorPos(&point);
ScreenToClient(mHwnd, &point);
mState.X.abs = point.x;
mState.Y.abs = point.y;
}
else
{
mState.X.abs +=  mState.X.rel;
mState.Y.abs +=  mState.Y.rel;
}
mState.Z.abs +=  mState.Z.rel;

//Clip values to window
if( mState.X.abs < 0 )
mState.X.abs = 0;
else if( mState.X.abs > mState.width )
mState.X.abs = mState.width;
if( mState.Y.abs < 0 )
mState.Y.abs = 0;
else if( mState.Y.abs > mState.height )
mState.Y.abs = mState.height;

//Do the move
if( mListener && mBuffered )
mListener->mouseMoved( MouseEvent( this, mState ) );
}
}


从上面的代码可以看到在获取完数据后会调用_doMouseClick函数来处理鼠标事件,

在往后可以看到在所有都处理完成后会判断如果是缓冲模式的话,调用mouseMoved将鼠标事件传递下去。

在_doMouseClick函数中也和mouseMoved类似会做缓冲和非缓冲的判断。

鼠标,键盘,手柄的处理有相似的地方,就不做讨论了。

关于OIS的使用,我想如果理解了OIS的具体实现,要使用起来肯定很容易,在此就暂时不做讨论了。

下面的链接是一个OIS的鼠标和键盘的示例代码链接。

http://download.csdn.net/detail/love0_0xin/4171517

转载请注明
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: