??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
参考:(x)ShowMesh,MAGE,两款工具源码
先把囄出来Q有旉文字ȝ一下,隑ֺ虽然不高Q但也折腾不时?也算q来学习(fn)Qt与Ogre的一个小l?br>
}
首先QRibbonTrail创徏?jin)两个链Q每个最多包?0个元素,ַ?00个世界单位。创Z(jin)一个结点animNoade。ؓ(f)q个l点创徏?jin)一个动?使得q个l点可以Ҏ(gu)关键帧的设计在场景中不断变化位置。ƈ把一个RibbonTrail与这个结点联lv来,于是RibbonTrail可与animNode一赯动。又创徏?jin)一个灯Qƈ联结到这个结点,灯随l点也一赯动,于是怪物头会(x)有明暗变化。最后用一个布告板来Ş象表C刚创徏的灯Q因为灯在场景中是不可见的。因为灯被挂到animNodel点上,所以布告板bbs也挂到同L(fng)l点上。当然别忘了(jin)使用?jin)动ȝ态来推进动画?/p>
}
void postRenderTargetUpdate(const RenderTargetEvent& evt)
{
// Show plane
mPlaneEnt->setVisible(true);
}
最后要提的是,在每一帧,都要使两个相机的位置Q朝向保持一?
frameStarted:
mReflectCam->setOrientation(mCamera->getOrientation());
mReflectCam->setPosition(mCamera->getPosition());
初始?/font>
在初始化之前Q必d一个渲染窗口。因为在分析脚本时可能会(x)创徏GPU资源Q而后者需要渲?br />上下文?br />// initialize all of the previously defined resource groups
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
// or, alternately, initialize the defined resource groups one at a time
ResourceGroupManager::getSingleton().initialiseResourceGroup("General");
ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");
卸蝲
可以在Q何时候卸载资源(以组或单个的方式Q?正在使用的资源不?x)被卸蝲?br />以组的方式卸?/font>
ResourceGroupManager::getSingleton().unloadResourceGroup("Bootstrap", true);
ResourceGroupManager::getSingleton().
unloadUnreferencedResourcesInGroup("Bootstrap", true);
true表示只卸载资源数据,不删除资源实例,它可以被reloaded。有些资源在创徏时被标ؓ(f)
"nonreloadable",q种cd的资源不能用上q方法卸载?br />清理或销毁资源组
清理仅是卸蝲资源与分资源烦(ch)引,销毁不仅做清理的工作,q包括从资源l中把自q除?br />ResourceGroupManager::geSingleton().clearResourceGroup("Bootstrap");
ResourceGroupManager::geSingleton().destroyResourceGroup("Bootstrap");
以个体方式卸?/font>
// assume that pEntity is a valid pointer to an instance of Entity
MeshPtr meshPtr = pEntity->getMesh();
meshPtr->unload();
加蝲/重蝲资源l?/font>
ResourceGroupManager::getSingleton().loadResourceGroup("Bootstrap", false, true)
二个布尔变量不同资源cd资源加蝲开养I一针对"Normal"资源Q二针对"World geometry"
资源l加载通知
class LoadingProgressListener : public ResourceGroupListener
{
public:
// fired when a group begins parsing scripts
void resourceGroupScriptingStarted(const String& groupName,
size_t scriptCount) {}
// fired when a script is about to be parsed
void scriptParseStarted(const String& scriptName) {}
// fired when the script has been parsed
void scriptParseEnded() {}
// fired when all scripts in the group have been parsed
void resourceGroupScriptingEnded(const String& groupName) {}
//q有一些接?br />}
//实现之后Q进行注?br />LoadingProgressListener listener(m_progressMeter);
ResourceGroupManager::getSingleton().addResourceGroupListener(&listener);
U理别名
在源材质被cloneӞ每个texture unit可以被赋于一个Texture alias(别名Q。可以用q个别名来指定用什么纹理。格式:(x) texture_alias <name> ~省Q?texutre_unit <name>( U理单元的名?
texture_unit DiffuseTex
{
texture diffuse.jpg
}
在这U情况下Qtexture_alias为DiffuseTex.
material TSNormalSpecMapping
{
technique GLSL
{
pass
{
texture_unit NormalMap
{
texture defaultNM.png
tex_coord_set 0
filtering trilinear
}
texture_unit DiffuseMap
{
texture defaultDiff.png
filtering trilinear
tex_coord_set 1
}
texture_unit SpecMap
{
texture defaultSpec.png
filtering trilinear
tex_coord_set 2
}
}
}
technique HLSL_DX9
{
pass
{
texture_unit
{
texture_alias NormalMap
texture defaultNM.png
tex_coord_set 0
filtering trilinear
}
texture_unit
{
texture_alias DiffuseMap
texture defaultDiff.png
filtering trilinear
tex_coord_set 1
}
texture_unit
{
texture_alias SpecMap
texture defaultSpec.png
filtering trilinear
tex_coord_set 2
}
}
}
}
例子中有两个技术,都用相同的U理,W一个技术用缺省的方式定义?jin)纹理别名,W二个技术显式地指定U理别名。两者都有相对应的同L(fng)U理别名。如果想定义一个材质只是想使用不同的纹?那么copy父材质时可以使用set_texture_alias来重新指定新U理?br />material fxTest : TSNormalSpecMapping
{
set_texture_alias NormalMap fxTestNMap.png
set_texture_alias DiffuseMap fxTestDiff.png
set_texture_alias SpecMap fxTestMap.png
}
上面代码对拥有指定别名的texture unit所使用的纹理图片进行了(jin)重新指定?br />
我们知道地ŞL起伏不^的,当主角在上面行走旉要根据地形的高度调整Q可以光U查询来实现?br />原理比较单:(x)向主角脚下执行光U查询,与地形有一个交点,Ҏ(gu)交点的高度调整主角位|?br />Terrain Clamping
void Entity::clampToTerrain() {
static Ogre::Ray updateRay;
updateRay.setOrigin(m_controlledNode->getPosition() + Ogre::Vector3(0, 15, 0));
updateRay.setDirection(Ogre::Vector3::NEGATIVE_UNIT_Y);
m_raySceneQuery->setRay(updateRay);
Ogre::RaySceneQueryResult& qryResult = m_raySceneQuery->execute();
if (qryResult.size() == 0) {
// then we are under the terrain and need to pop above it
updateRay.setOrigin(m_controlledNode->getPosition());
updateRay.setDirection(Ogre::Vector3::UNIT_Y);
m_raySceneQuery->setRay(updateRay);
}
qryResult = m_raySceneQuery->execute();
Ogre::RaySceneQueryResult::iterator i = qryResult.begin();
if (i != qryResult.end() && i->worldFragment)
{
Ogre::SceneQuery::WorldFragment* wf = i->worldFragment;
m_controlledNode->setPosition(m_controlledNode->getPosition().x,
i->worldFragment->singleIntersection.y,
m_controlledNode->getPosition().z);
}
}
void Entity::init()
{
// lots of other irrelevant entity init stuff goes here
m_raySceneQuery = sm->createRayQuery(
Ogre::Ray(m_controlledNode->getPosition(),
Ogre::Vector3::NEGATIVE_UNIT_Y));
// move this node is such a way that it is above the terrain
clampToTerrain();
}
下列参数用于提供自己的着色程序时使用Q这?x)提供自己定义的materialQ那么先前定义的
WorldTexture ?DetailTexture的设|不再用刎ͼ多余的了(jin)?/p>
MorphLODFactorParamName=morphFactor
//假设VertexProgramMorph被设为yes,定制的material中包括一个高U顶点程序。它指定?jin)一个顶?br />//E序的参数名Q这个参数用于融合LOD,参数g0Q?Q?表示不调_(d)1表示完全调整C一ULOD
MorphLODFactorParamIndex //用于materail中包含低U顶点程序的情况Q意义同?br />CustomMaterialName //指定的materail名字
上述配置文g定义?jin)基于高度图的地形。这些参数定义可概括Zc:(x)Ogre使用W一cM高度图生地?br />mesh与材质。第二类是定制材质与GPU点E序Q这可以代替ogre自动产生的着色程序?br />另外的说明:(x) Ogre::DataStreamPtr Process_Loader::getSceneDataStream(SceneData &data) {
TerrainScenceManager?x)把高度囑ֈ为向个pages,每个page由几个tilesl成.而它们也不过是个方便的名字,
它们都定义了(jin)在生的mesh中一l构成正方Ş的顶炚w?br />WorldTexture定义的纹理不必与目标地Ş一样大?br />PageWorldXQPageWorldZ可以~放世界中的地Ş?br />MaxHeight 在Y方向~放地Ş?br />DetailTexture 只用一个纹理,如用多层纹理,应该使用自定义materail?br />
从程序加载地?/font>
setWorldGeometry()有重载Ş式,一U用于加载配|文Ӟ另一U我们可在程序中使用Q以
辑ֈ手工加蝲的功能。这里,SceneData?typedef 为std:map,它存储了(jin)如我们在terrain.cfg
中看到那些值对。假设我们已l从某个二进制文件读入我们想要的内容到SceneData中。我们要?br />的就是把d的内容{换成setWorldGeometry()需要的cd。先看一下函数原型:(x)
Ogre::SceneManager::setWorldGeometry ( DataStreamPtr & stream,
const String & typeName = StringUtil::BLANK
)
DataStreamPtr是一个智能指?因此从局部变量中q回是安全的,E序相当直观Q不再多说了(jin)?/p>
// create what looks like a config file for the edification of Ogre
std::string mem;
SceneData::iterator it;
for (it=data.begin(); it!=data.end(); it++) {
mem += it->first;
mem += "=";
mem += it->second;
mem += "\n";
}
void *pMem = (void *)new unsigned char[mem.length()+1];
memset(pMem, 0, mem.length()+1);
memcpy(pMem, mem.c_str(), mem.length() + 1);
// stuff this into a MemoryDataStream
Ogre::DataStreamPtr pStr(new Ogre::MemoryDataStream(pMem, mem.length() + 1));
return pStr;
}
// and then elsewhere in the world loader:
Ogre::DataStreamPtr pStr = getSceneDataStream(terrainDef);
m_sceneMgr->setWorldGeometry(pStr);
]]>
场景理器类?br />
以分析源码的方式讨论一下插件的加蝲机制与特定场景管理器是如何进行运用的?br />上一章提C(jin)以手工的方式初始化ogre,包括手工加蝲场景理器:(x)
root->loadPlugin("Plugin_OctreeSceneManager");
其实所谓的自动方式下,Root:Root()中也?x)间接调用到l(f)oadPlugin()Ҏ(gu)Q这已在前面的笔?br />(配置文gPlugins.cfg )中提到过。既然特定管理器以插件的形式Qdll文gQ给出,下面先看如何
加蝲dll. q入源码Q标可明执行顺序?br />void Root::loadPlugin(const String& pluginName)
{
// Load plugin library
DynLib* lib = DynLibManager::getSingleton().load( pluginName ); //(1)
// Store for later unload
mPluginLibs.push_back(lib); //(4)
// Call startup function
DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->getSymbol("dllStartPlugin"); //(5)
// This must call installPlugin
pFunc(); //(6)
}
DynLib* DynLibManager::load( const String& filename) //(2)
{
DynLib* pLib = new DynLib(filename);
pLib->load();
mLibList[filename] = pLib;
return pLib;
}
void DynLib::load() //(3)
{
m_hInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() );
}
W?font color="#ff0000">(3)中的宏定义如下:(x)
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
# define DYNLIB_HANDLE hInstance
# define DYNLIB_LOAD( a ) LoadLibrary( a )
到此QDLL被加载到内存,W?font color="#ff0000">(4)步,mPluginLibs是个STL容器Q它存放动态库指针?br />W?font color="#ff0000">(5)步,q入源码可以看到
void* DynLib::getSymbol( const String& strName ) const throw()
{
return (void*)DYNLIB_GETSYM( m_hInst, strName.c_str() );
}
其中宏定义:(x)define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b )Q很昄Q它取得一个名?br />dllStartPlugin的函数指针:(x)不防再看看宏定义Q?typedef void (*DLL_START_PLUGIN)(void);
说明DLL_START_PLUGIN为参Cؓ(f)I,q回gؓ(f)I的函数指针?br />每个注册到ogre的dll都实C(jin)q个U定函数。对于我们当前讨论的场景理器Plugin_OctreeSceneManager
来讲Q我们可以找到其相应的定?它在W?font color="#ff0000">(6)步中执行
OctreePlugin* octreePlugin;
extern "C" void _OgreOctreePluginExport dllStartPlugin( void )
{
// Create new scene manager
octreePlugin = new OctreePlugin(); //(6-1)
// Register
Root::getSingleton().installPlugin(octreePlugin); //(6-2)
}
E序执行?font color="#ff0000">(6-1)步,new 来一个OctreePlugin,我们看一下它的定义:(x)
class OctreePlugin : public Plugin
{
public:
OctreePlugin();
const String& getName() const;
void install();
void initialise();
void shutdown();
void uninstall();
protected:
OctreeSceneManagerFactory* mOctreeSMFactory;
TerrainSceneManagerFactory* mTerrainSMFactory;
TerrainPageSourceListenerManager* mTerrainPSListenerManager;
};
我们?x)注意到它包含两个工厂类指?OctreeSceneManagerFactory,TerrainSceneManagerFactory
他们是用来生特定ScenManager的,E后讨论。先?font color="#ff0000">(6-2)步:(x)
void Root::installPlugin(Plugin* plugin)
{
mPlugins.push_back(plugin); //(6-2-1)
plugin->install(); //(6-2-2)
// if rendersystem is already initialised, call rendersystem init too
if (mIsInitialised)
{
plugin->initialise(); //(6-2-3)
}
}
//(6-2-1)步把插g攑ֈ容器中。看?font color="#ff0000">(6-2-2)做了(jin)什么:(x)
void OctreePlugin::install()
{
// Create objects
mOctreeSMFactory = new OctreeSceneManagerFactory();
mTerrainSMFactory = new TerrainSceneManagerFactory();
mTerrainPSListenerManager = new TerrainPageSourceListenerManager();
}
呵,刚才q说两个工厂cL针,现在把两个工厂徏hQ以后可以用来生产东西了(jin)?br />l箋看看(6-2-3):
void OctreePlugin::initialise()
{
// Register
Root::getSingleton().addSceneManagerFactory(mOctreeSMFactory);
Root::getSingleton().addSceneManagerFactory(mTerrainSMFactory);
}
哦,把这两个工厂注册到Root中,让Root可以使用它们。啊q句话有炚w题,Root只是间接的用刎ͼ
直接雇主?SceneManagerEnumerator* mSceneManagerEnum;它被包含在Root中。于是我们可以看?br />void Root::addSceneManagerFactory(SceneManagerFactory* fact) //(6-2-3-1)
{
mSceneManagerEnum->addFactory(fact);
}
工厂已经有了(jin)Q我们如何利用这个工厂生产出我们惛_的东?SceneManager)呢?
回忆上一章的手工初始化过E中Q我们一般用以下语句来创建SceneManager:
SceneManager *sceneMgr = root->createSceneManager(ST_GENERIC); //(A)
也可以这L(fng)
sceneMgr = ogre->createSceneManager("OctreeSceneManager"); //(B)
每个工厂c都用一个字W串表示其类型。上面说的两个工厂分别用的字符串ؓ(f)Q“TerrainSceneManager”,"OctreeSceneManager"
(A)语句肯定?x)调用工厂类的方法来产生实际的SceneManager,下面看源码验证一下:(x)
SceneManager* Root::createSceneManager(const String& typeName,
const String& instanceName)
{
return mSceneManagerEnum->createSceneManager(typeName, instanceName);
}
l箋挖:(x)
SceneManager* SceneManagerEnumerator::createSceneManager(
const String& typeName, const String& instanceName)
{
SceneManager* inst = 0;
for(Factories::iterator i = mFactories.begin(); i != mFactories.end(); ++i)
{
if ((*i)->getMetaData().typeName == typeName)
{
if (instanceName.empty())
{
// generate a name
StringUtil::StrStreamType s;
s << "SceneManagerInstance" << ++mInstanceCreateCount;
inst = (*i)->createInstance(s.str());
}
else
{
inst = (*i)->createInstance(instanceName);
}
break;
}
}
}
上述代码很简单:(x)因ؓ(f)执行(6-2-3-1)已经q实际工厂类实例q行?jin)注册。于是遍历每个工厂实例,
扑ֈcd相符的。找C后,如果没有传场景管理器实例的名字,pv个名字。然后用q个名字
生Z个实例来。CreateInstance没干什么,new 呗?br />SceneManager* OctreeSceneManagerFactory::createInstance(
const String& instanceName)
{
return new OctreeSceneManager(instanceName);
}
是q样。OctreeSceneManager and TerrainSceneManager 的功能都是由一个DLL提供的。它们的关系是:(x)class _OgreOctreePluginExport TerrainSceneManager : public OctreeSceneManagerQ按照一般的cdp逻辑Q?br />我们知道zcM般功能都比父cd大,父类应用于一般场景,子类针对特定场景。这个逻辑在这里是对的。本来是写读书笔讎ͼ跑题?jin),打住?/p>
// tell Root not to load from any plugins or settings file
Root *root = new Root("", "");
// Load feature plugins. Scene managers will register
// themselves for all scene types they support
root->loadPlugin("Plugin_CgProgramManager");
root->loadPlugin("Plugin_OctreeSceneManager");
// load rendersystem plugin(s). The order is important in that GL
// should be available on on platforms, while D3D9 would be available
// only on Windows -- the try/catch will intercept the exception in this
// case where D3D9 is not available and continue gracefully.
try {
root->loadPlugin("RenderSystem_GL");
root->loadPlugin("RenderSystem_Direct3D9");
}
catch (...) {}
try {
// We'll simulate the selection of a rendersystem on an arbirtary basis; normally
// you would have your own code to present the user with options and select the
// rendersystem on that basis. Since a GUI is beyond the scope of this example, we'll
// just assume the user selected OpenGL.
RenderSystemList *rList = root->getAvailableRenderers();
RenderSystemList::iterator it = rList->begin();
RenderSystem *rSys = 0;
while (it != rList->end()) {
rSys = *(it++);
if (rSys->getName().find("OpenGL")) {
root->setRenderSystem(rSys);
break;
}
}
// check to see if a render system was selected; if we reached the end of the list
// without selecting a render system then none was found.
if (rSys == 0) {
delete root;
std::cerr << "No RenderSystem available, exiting..." << std::endl;
return -1;
}
// We can initialize Root here if we want. "false" tells Root NOT to create
// a render window for us
root->initialise(false);
// set up the render window with all default params
RenderWindow *window = rSys->createRenderWindow(
"Manual Ogre Window", // window title
800, // window width, in pixels
600, // window height, in pixels
false, // fullscreen or not
0); // use defaults for all other values
// from here you can set up your camera and viewports as normal
// get a pointer to the default base scene manager -- sufficient for our purposes
SceneManager *sceneMgr = root->createSceneManager(ST_GENERIC);
// create a single camera, and a viewport that takes up the whole window (default behavior)
Camera *camera = sceneMgr->createCamera("MainCam");
Viewport *vp = window->addViewport(camera);
vp->setDimensions(0.0f, 0.0f, 1.0f, 1.0f);
camera->setAspectRatio((float)vp->getActualWidth() / (float) vp->getActualHeight());
camera->setFarClipDistance(1000.0f);
camera->setNearClipDistance(5.0f);
// Run the manual render loop. Since we are not using a frame listener in this case, we
// will count to 15 seconds and then instead of exiting, we'll change the render window settings
// and re-initialize it.
bool renderLoop = true;
Timer *timer = Ogre::PlatformManager::getSingleton().createTimer();
timer->reset();
float s = 0.0f;
while (renderLoop && window->isActive()) {
renderLoop = root->renderOneFrame();
// accumulate total elapsed time
s += (float)timer->getMilliseconds() / 1000.0f;
// if greater than 15 seconds, break out of the loop
if (s >= 15.0f)
renderLoop = false;
// we must call the windowing system's message pump each frame to
// allow Ogre to process messages
//PlatformManager::getSingleton().messagePump();
}
}
catch (Exception &e) {
std::cerr << e.getFullDescription() << std::endl;
}
delete root;
return 0;
}
视口
通过视口上的一点与相机的原点生世界空间中的一条光U?br />// x and y are in "normalized" (0.0 to 1.0) screen coordinates
Ray getCameraToViewportRay(Real x, Real y) const;
视口,创徏多个视口Q通过Z序(高在上)(j) 定覆盖效果Q每个视口可以有不同的背景?br />// assume window is a valid pointer to an existing render window, and
// a valid pointer to an existing camera instance
Viewport *vpTop, *vpBottom;
// second parameter is z-order, remaining params are position and size,
vpBottom = window->addViewport(camera, 0);
// create a smaller viewport on top, in the center, 25% of main vp size
vpTop = window->addViewport(camera, 1,
0.375f, 0.375f,
0.25, 0.25);
// set the background of the top window to blue (the default is black
// need to set the bottom window explicitly)
vpTop->setBackgroundColour(ColourValue(0.0f, 0.0f, 1.0f));
// an alternate way to set the color is to use the manifest constant
// vpTop->setBackgroundColour(ColourValue::Blue);
在多视口情况下,overlay~省在每个视口中渲染。可以关掉。Skybox, Shadow也是如此?br />vpTop->setOverlaysEnabled(false);
vpTop->setSkiesEnabled(false);
vpTop->setShadowsEnabled(true);
子系l概?/font>
Root Object
Root 是程序进入点Q它是一个façade c,提供?jin)访问子pȝ的方便的Ҏ(gu)。通过它可以开启ogre,也可能通过它关闭ogre?/font>
资源理
在渲染场景中使用的Q何东襉K被视源。所有的资源最l都被一个单个类对象理QResourceGroupManagerQ它负责定位资源Q初始化资源Q不真正装蝲Q?
在缺省情况下QOgre认识以下cd的资源:(x)
Mesh: 二进制格式,它也可包?morph 和 pose 动画数据
Skeleton: 可以被Mesh文g引用Q也可单独用,包含骨骼层次l构信息Q关键信息?
Material: 定义?jin)渲染一l几何体时的渲染状态,可以被mesh文g引用Q也可以手工使用?
GPU E序Q?program 支持HLSL, GLSL,Cg 与低U的 .asm Q这些文件会(x)在Q?material之间被剖析,因此在一个material之中被引用的GpuE序L有效的?
Compositor: 与Material很相|扩展名不?
Font: 用字体定义文件去定义在overlay中用的字体Q扩展名 .fontdef
每种资源都有自己的特定的ResourceManager,如,MaterialManager, FontManager。ResourceGroupManager负责通过名字查找资源Q它不负责实际的内存理d。这些Q务是由ResourceManager基类来完成的。ResourceGroupManager允许通过l名来加载,释放整组资源?
默认情况下,Ogre认ؓ(f)资源以一个磁盘文件的形式存在。然而,有些cd的资源可以手工管理,当前只有mesh, font 有手工资源加载的实现。其他类型的可以自己实现QOgre已经在它的框架中保留?jin)这U功能?
场景理
所有的具体实现都从SceneManagerz而来Q程序中怸此类交互。,可以有多个活动的SceneManager。它们用来管理SceneNode, SceneNode可以在场景中被移动。SceneNode也可以有层次l构?
场景实际内容l常也Entity实例的Ş式存在。它们被Scene Manager创徏QMovableObject实现。一个有效的Entity可以被attach 到SceneNode上面。Entity l常从磁盘上?mesh文g加蝲。然后也可以手工创徏内容对象Q如plane.当Entity被attach到SceneNode上时Q进行移动等操作是针对SceneNode的,而不是内容对象?
也可以把非内容对?灯,相机{?attach 到SceneNode上面?
渲染pȝ与渲染对?
RenderSystem 是Ogre与底层API的接? RenderTarget 是 渲染H口与渲染纹理的概括?
Ogre 扶持多个渲染H口。窗口可以通过Root对象自动、手动地创徏Q也可能通过RenderSystem创徏?
Ogre 理?/font>
Manager 是一个可以访问相关类型对象,理其生命周期的cR例如,ArchiveManager理Archive实现的创Z注册Q访问注册的Archive实现实例。Root对象创徏的副作用之一是初始化所有ogre Manager对象?
LogManager : 发送日志信息到输出?
ControllerManager: 理 controllers,后者是Z各种输入Qؓ(f)别的cM生状态g供用?
DynLibManager: 理动态链接库
PlatformManager: 把抽象的讉K转换成底层硬件与操作pȝ相关的细节?
CompositorManager: 讉K理 Compoitor framework.
ArchiveManager: 文gpȝ目录QZIP文g
ParticleSystemManager: _子pȝQ发器Q媄(jing)响器QaffectorQ?
SkeletonManager: 允许同名的skeleton对象重用
MeshManager : 理mesh, 允许同名mesh重用
HighLevelGpuProgramManager: l护Q加载,~译高GPUE序
GpuProgramManager: l护低GPUE序Q把~译的高UGPUE序转ؓ(f)汇编
ExternalTextureSourceManager: 理外部U理源类实例Q如视频?
FontManager: 理Overlay中用的字体
ResourceGroupManager: 加蝲Q生命期理Q所有注册的E序资源
OverlayManager: 加蝲Q创建,2D Overlay cd?
HardwareBufferManager: 理׃ng~冲Q 点~冲Q像素缓Ԍ索引~冲{?
TextureManager: 理U理
ȝQ?q一章无意覆盖关于Ogre的所有东西,只说明了(jin)一些普遍需要的Ogre对象Q和一些不太常打交道的ogre 对象?
设计哲学
传统上,使用 Direct3D ?OpenGL 来渲染场景和对象Q需要遵循一pdE序处理的步骤Q调?API 讄渲染状态,调用 API 发送几何体信息Q通知 API ?GPU L染几何体。对每个几何体都是如此返复,直到当前帧被完全渲染。在一下同样如此?
使用面向对象的方法来渲染几何体简化了(jin)上述q程。通过处理l成场景的各U对象而不是原始的几何体。这些对象包括:(x)可运动对象,?rn)态对象(构成世界布局Q,光,相机{。那?3D API 则不再需要:(x)只是把这些对象放到场景中Q?Ogre 负责处理琐碎的细节。而且Q可以用更加直观的方法来操纵对象Q而不是用矩c(din)ȝ说来Q我们可以处理对象,它的属性,调用更直观的Ҏ(gu)Q而不再用点列表Q三角Ş列表Q旋转矩늭手段来管理了(jin)?
Ogre 提供?jin)面向对象的框架Q包括了(jin)对象模型中的渲染处理的所有部分。渲染系l抽象了(jin)底层 3D API 的复杂性。场景图形功能被抽象成单独的接口Q这样一来,各种不同的场景图形管理算法可以即插即用。在场景中的所有可渲染对象Q无论是可移动的Q还是静(rn)态的Q都被一l公共接口抽象,q些接口提供?jin)实际的渲染操作Q例?techniques 和它所包含?passes Q?.
设计亮点
一“设计模式”的合理使用
Ogre 中大量用了(jin)l典的设计模式。例如,使用?Observer ”模式来通知应用E序特定事g的发生,?demo ?FrameListener 的用得程序可以接收 frame-started and frame-ended 事g通知。?Singleton ”模式强制实现类的单实例。?Iterator ”模式用来遍历数据结构的内容?
“Visitor?模式用来在对象上执行某种操作?Façade 模式用来抽象底层Q提供一个单一cL口?
?Factory? 用来创徏实例?
?场景图与内容分离
传统上,场景图与内容同处于同一l承体系下。它要求从场景结点子cd内容c,也就是说内容cM场景l点l承。实践证明这U设计非常糟p?首先Q?Ogre 操作场景囑Ş在接口层 , 它不假设特定囑Ş法的实现。实际上Q?Ogre 操作场景囑Ş仅仅通过它的{Q方法)(j)Q完全忽略底层的法实现?
W二Q?Ogre 的图形接口只兛_(j)囑Şl构Q不包含M内在的访问或理功能。后者被推到 Renderable 中,场景中的所有几何( movable or otherwise Q都从它l承。这?Renderalbes 的渲染属性(材质Q?materials Q被包含?Entity 对象中去?Entity 可以包含一个或多个 SubEntity 。这些子实体是实际的可渲染对象。下面是各对象的一个关pdQ?br />
通过q样的设计,场景理与场景内容充分解藕。过于场景图来说Q通过 Movable Object 几何Q渲染属性变得有效。注意到 Movable 不是?Scene Node l承而来的?Movable Object ?attached ?Scene Node 上去?. 当扩展,改变Q重构场景图形实现时对场景内容对象的设计与实现没有Q何媄(jing)响?
从另一斚w来说Q由于场景图形不需要知道内容类的Q何变化。只要实C些简单的场景囑Ş“确实“需要知道的一些接口。因此,我们可以L?attached 一些自定义的东西到场景l点上,比如声音信息{。(听说有个 OgreAL, 它包装了(jin) OepnAL Q猜惛_是这样做的吧Q?
三 插gl构
OGRe 被设计成可扩展的?Ogre 通过“基于契U的设计 ?来完成?Ogre 可以被设计成一l共同工作的lgQ它们通过一些已知接口来怺交流。这带来极大的灵zL,某种功能的不同实C改变非常Ҏ(gu)。D例来Ԍ׃ ogre 处理场景囑օ理是在接口U,用户可以随意的选择特定法的实现。而且Q当用户创徏一个新的实玎ͼ可以很容易的以插件的形式提供l?Ogre, 实现时只要遵?ogre 定义的一些接?. File archives , render systems 也以插g的Ş式提供,_子pȝ也是?插g形式的吸引h之处是,Z(jin)加入插gQ不需要重新编?Ogre ?. 插g可以在运行时加蝲?
四 g加速的渲染支持
Ogre 被设计成只支持硬件加速图形渲染,直接软g渲染不是它的选项?Ogre 使用完整的硬件加速能力,包括可编E?shaders ?Unreal 引擎能做什么, Ogre p做什么!Q( ^_^, 作者说的)(j)。引擎直接支持的全局光照 (precomputed Radiance Transfer {?) q没有实玎ͼ因ؓ(f)q些多数用于非实时计场合,因此不是什么问?.Ogre 现在和未来将可能只?Direct3D ?OpenGL ?
五,灉|的渲染队列结?
Ogre 采取一个新Ҏ(gu)来解军_景中各部分渲染次序问题。粗略地看,标准q程通常如下工作Q渲染地形或世界几何Q渲染可Ud对象Q渲染各U特效,渲染 OverLay, 渲染背景或天I盒 . 然而,正如典型的实CP像集成电(sh)路处理模块)(j)Q这个过E很难改?. 在许多例子中Q?很难改变渲染的顺序,D难以l护与不灉|的设计的?渲染队列的概忉|Q?Ogre 以一ơ一个的方式渲染一l可以排序的队列Q也已同L(fng)方式渲染每个队列中的内容。也是队列本n有优先Q同样队列中的对象也有自q优先U?br />
上图中,队列由后向前渲染Q?Background à OveryLay Q?, 在最前的队列中从左至x染?
在这U机制下重新l织渲染序非常单,只要重新分配优先U就可以?jin)。队列可以定Ӟ队列中的对象也可以。整个队列可以?turned on?and “turned off?Q队列中的对象也可以?每个队列提供?jin)事仉知Q如 prerender and postrender Q?, 应用E序有机?x)改变队列中的对象渲染。这个机制对于开发和l护复杂E序都相当有用?
六 健壮的材质系l?
Ogre 的材质由一个或多个 technique l成Q?technique ?pass 的集合?Pass 是材质别的渲染单元Q导致一ơ图形硬件的 ”draw?调用。可以有多个 pass, 每个 pass 是一ơ全新的渲染操作Q包括硬件渲染状态改变?
最吸引人的Ҏ(gu)是 automatic fallback ?Ogre 自上向下圎ͼ technique 列出的顺序)(j)L最适当?technique.Ogre q能力地重新组l?technique 中的 pass, 以迎合最的技术需求。例如,当前g只支持单U理单元Q而你E序中最复杂度的技术,需要至两个纹理单元,那么 ogre ?x)把一?pass 拆分成两个,通过混合两个单元U理的方式达到同L(fng)效果?
Ogre 材质pȝ也支?schemes 的概c(din)?Schemes 可以理解作ؓ(f)普遍的“非帔RQ高Q中Q低“不同等U的囑Ş讄。在q种情况下,可以定义四个 schemes , qؓ(f)每一个分?techniques( 复数 ) 。这样就可以 ogre 的所?technique fallback 查找那些属于特定 schemes ?techniques 。得材质管理更Ҏ(gu)炏V?
用材质脚本完成的事,同样也可以用代码的方式完成。代码的方式也支?schemes ?technique fallback ?
七 本地优化的几何与骨骼格式
Ogre 使用它单独的 mesh 与骨骼格式。不能直接加载第三Y件包生成的数据。社Z有{换第三文件格式的工具Q但它不?Ogre 库的一部分?Ogre 使用自己的格式是Z(jin)效率?
八 多种cd的动?
Ogre 支持三种cd的动画:(x)骨骼动画Q变形动画, Pose 动画
骨骼动画Q把点l定到骨g。对象的每个点可以受到四个独立的骨头媄(jing)响。每U媄(jing)响被赋于一个用数量表示的权重。骨骼动d在关键上执行前向运动学模式?Ogre 当前不支持逆向动画 .
Morph 动画是一U顶点动L术,他储存关键上的l对的顶点位|信息,在运行时在两个关键之间插倹{这?Pose 动画不同Q?Pose 动画存储的是偏移数据而不是绝对位|?Pose 动画可以混合以Ş成复杂动甅R?Morph 动画?Pose 动画功能有限Q因Z存储的是l对|因此难以与其?Morph 动画混合?Morph 动画?Pose 动画之间也不能?.
所有的动画cd都可以与骨骼动画混合使用 .
所有动ȝ型都可用Y件执行或是利?GPU g执行?
Ogre 动画是基于关键的,内插方式有线性与立方hq两U选择?
?ji) Compositor Postprocessing ( 怎么译 ~?)
用来在一个视口中创徏二维的,全屏的后处理效果 . 例如模糊Q黑白电(sh)视,{等效果Q?Ogre 有个很好的示例)(j) Compositor 也有cM于材质系l的 technique ?pass 的概c(din)在视口最l被输出前,多个计算与渲染可被执行。像材质?fallbacks,Compositor framework 提供?fallback 处理Q例如,当输出的像素格式无效时。理?compositor 最Ҏ(gu)的方法是把它当成片段E序Q像素渲染)(j)的扩展。不同之处在于:(x)传统的图形管U每个材?pass 只充怸个片D늨序,?Compositor framework 则可以像打乒乓球一h来回回好多次?Compositor 脚本扫行?ViewPort 上,因此它可以面向Q何的 render target, 可以是可渲染U理Q主Q副渲染H口。与材质pȝ一P它除?jin)利用脚本实玎ͼ也可以用代码实现?
十 ?扩展资源理
?ogre 中,资源定义为:(x)渲染几何体到可渲染目标上用到M东西。很明显包括Q?mesh, skeleton, material , overlay script , font , Compositor script, GPU program , texture ?
每种资源都有自己?manager, 它主要负责控制特定类型资源在内存中的占用数量。换句话_(d)它控制着资源实例的生命期。不q仅仅在一点上控制Q首先,它只能储存同样多的实例,取决于这U类型的资源所分配的内存。第二, ogre 不会(x)删除正被其他部分引用的实例?
资源本n实际负责加蝲自己。这是资源系l的一个设计特性:(x)手工资源加蝲。手工加载意味着资源加蝲Q处理,是利用一个方法调用实现的而不是从文gpȝ中隐式地加蝲?
资源?ogre 中有四种状态:(x)未定义, 声明Q未加蝲Q 加蝲?
未定义,说明 ogre 对它一无所知?
声明Q它已经在档案中q行?jin)?ch)引?
未加载,已经q行?jin)初始化Q脚本,已经被解析过?jin)?j)Q引用已l被创徏
加蝲Q已l在它的资源 manager 所理的内存池中占用空间了(jin)?
可以用“组”来理理各类资源。组中资源之间的关系是Q意的Q完全取决于E序员:(x)创徏 GUI 的所有资源可以分Zl,?A 字母开头的资源也可以分Zl,{等。有?~省的组QGeneral
?ogre 中查找某个资源实例时Q不?x)考虑Q资源所在的l。有Ӟ我们可以资源组命当成某U的 ?命名I间来用“?
Archives 中的资源是非手动加蝲的?Archives 是普通文件容器的单抽象。它包括文gpȝ?ZIP 档案。用户可以实现自定义cd?archive ?
在以前的W记中已l对 CEGUI 的用做?jin)简单的介绍Q这里ؓ(f)?jin)完整性还是把?C Q?P 一下:(x)
使用 cegui 来制作界?, 不论在何U^C , 有基本的三大步骤要做 :
1, 创徏一?CEGUI::Render 实例
2, 创徏 CEGUI::System 对象
3, 调用各种Ҏ(gu)来渲染用L(fng)?
W一?, 在我使用?ogre 环境下用以下代码来创徏 CEGUI::Render 实例
Ogre3D
CEGUI::OgreCEGUIRenderer* myRenderer =
new CEGUI::OgreCEGUIRenderer(myRenderWindow);
W二步相当简?, 可?new CEGUI::System(myRenderer);
W三步,基本上来Ԍ大部分^CQ如 direct3D, OpenGL, 我们在渲染@环的N调用 CEGUI::System::renderGUI 来开始界面的渲染。如果我们?ogre3d 引擎Q这一步不需?我们昄的执行?创徏 CEGUI H口Q我们可以用两UŞ式,一?C Q+代码Q二是编?XML layout 文g?
CEGUI 本n侦测用户输入Q这些不?CEGUI 的责任,而是E序的员的责仅R当有用户外部输入时Q我 们可以选择这些消息告?CEGUI Q这?CEGUI 才会(x)响应?
CEGUI 使用回调机制来进行消息处理。可以ؓ(f)某个H体的特定事件注册一个函敎ͼ当窗体事件发生时Q?CEGUI ?x)自动调用所注册的函数?
DEMO 中需要注意的?Render To Texture 的实现?
先创建在 ogre 中的 RTT:
RenderTexture * rttTex = mRoot->getRenderSystem()->createRenderTexture( "RttTex", 512, 512, TEX_
TYPE_2D , PF_R8G8B8 );
再{换成CEGUI可识别的:
// Retrieve CEGUI texture for the RTT
CEGUI::Texture* rttTexture = mGUIRenderer->createTexture((CEGUI::utf8*)"RttTex");
Ҏ(gu)q个Texture生成?jin)一个ImageSet;
在ImageSet中定义了(jin)一个ImageQ名为RttImageQ?其大与上边的Texutre同样大小?
当在H口中增加一个static ImageӞ把其image属性设??RttImage ?卛_?
1. 在主E序cȝ Application :: createScene ( ) 场景创徏Ҏ(gu)中进行以下工作:(x)
Animation * Ogre::SceneManager::createAnimation (
const String & name, // 动画名称
Real length // 动画长度(U?
) [virtual]
(2) 使用 createTrack Ҏ(gu)为动dq动画:(x)
AnimationTrack * Ogre::Animation::createTrack (
unsigned short handle, // 分配l轨q动ȝ索引句柄Q用于以后可能的调用
Node * node // 指定要沿着q条轨迹q动的节?br /> )
(3) 使用 createKeyFrame Ҏ(gu)q动dZpd关键帧:(x)
KeyFrame * Ogre::AnimationTrack::createKeyFrame (
Real timePos // 旉位置
)
(4) 使用 setTranslate ?setScale ?setRotation 三个Ҏ(gu)来设|关键每个旉位置上节点的位置、羃放、旋转属性:(x)
void Ogre::KeyFrame::setTranslate ( const Vector3 & trans )
void Ogre::KeyFrame::setScale ( const Vector3 & scale )
void Ogre::KeyFrame::setRotation ( const Quaternion & rot )
(5) 使用 createAnimationState Ҏ(gu)创徏一个动ȝ态来q踪q个轨迹Q?br /> AnimationState * Ogre::SceneManager::createAnimationState ( const String & animName ) [virtual]
(6) 使用 setEnabled Ҏ(gu)来激zdȝ态:(x)
void Ogre::AnimationState::setEnabled ( bool enabled )
2. 在接收器cȝ Listener::frameStarted(const FrameEvent& evt) Ҏ(gu)中刷新动ȝ态:(x)
void Ogre::AnimationState::addTime ( Real offset )
资源?OGRE 应用E序渲染q程中需要用到的U理囄、网格模型文件、骨骼动L件的ȝ?OGRE 应用E序需要在渲染前将q些资源载入内存Q那需要让 OGRE 引擎知道资源的搜索\径。特别的?OGRE 引擎支持直接d Zip 压羃文g中的内容Q所?Zip 文g也必被当成搜烦(ch)路径来指定。在 OGRE 引擎中具有虚拟文件系l的概念Q引擎内部蝲入资源文仉是通过虚拟文gpȝ来进行的Q引擎ƈ不关?j)资源文件来自一个普通文件夹?zip 压羃包甚至网l映。真正的文gd功能是通过插g来实现的Q所以大家在q行环境里可以发?Plugin_FileSystem.dll Q早期的 OGRE 版本q有 Plugin_Zip.dll Q在新的版本里被实现到引擎内部了(jin)。目前还没有实现对网l文件的直接讉K?
Z(jin)方便 OGRE E序在运行期间查找资源,使用?jin)资源配|文?resources.cfg 。这是一个文本文Ӟ我们可以?OGRE E序的可执行文g的同一文g夹下扑ֈ它。它的内容就是对资源路径的指定,CZ如下Q?
Zip=../../../Media/dragon.zip
Zip=../../../Media/knot.zip
Zip=../../../Media/skybox.zip
FileSystem=../../../Media/
如果资源在一?Zip 文g中,写 Zip=****** Q如果资源在一个普通的盘文g多w写 FileSystem=****** Q通常q两U情况都有。例如在 OGRE 自带?Demo 中,将大部分资源放在一个文件夹里,Ҏ(gu)的资源该文g夹中?Zip 文g里?
?OGRE 自带的例子框?setupResources() 展示?Resources.cfg 文g的用:(x)先利?ConfigFile cd文g内容q行?jin)解析,资源目录用ResourceGroupManager:: addResourceLocation()向OGREq行提交;
Plugins.cfg
Ogre的许多功能是以插件的形式提供?Ogre提供的以Plugin_开头的许多.DLL文g都是所谓的插g?br />Plugins.cfg指定?jin)插件的路径和插件文件? 它们可以攑֜其它文g多wQ但必须在本文g里指定\径?/p>
?windows q_插g的装入过E如下:(x)
Root::()
{
if(!pluginFileName.empty())
loadPlugins(pluginFileName);
?
――――――>
void Root::loadPlugins(const String& pluginsfile)
{
ConfigFile cfg;
cfg.load(pluginsfile);
...........................
// 解析文gQ处理后目录与文g名联?
for(;;)
loadPlugin(plugindir + (*it))
} ――――――>
Root::loadplugin(const string& pluginName)
{
DyLibmanager::getsinleton.load(pluginName);
}
――――――>
DynLibManager::load(const string& filename)
{
DynLib* pLib=new DynLib(filename);
pLib->load();
}
――――――>
void DynLib::load()
{
m_hInst=(DYNLIB_HANDLE)DYNLIB_LOAD(name.cstr());
}
?windows q_下有如下定义Q?
#define DYNLIB_LOAD(a) LoadLibrary(a)
到此Q?x.dll 插g被加载到内存中,可以使用插g的功能了(jin)^_^
以下Z个典型的 Plugins.cfg 文g的内容:(x)
# Defines plugins to load
# Define plugin folder
PluginFolder=.
# Define plugins
Plugin=RenderSystem_Direct3D9
Plugin=RenderSystem_GL
Plugin=Plugin_ParticleFX
Plugin=Plugin_BSPSceneManager
Plugin=Plugin_OctreeSceneManager
Plugin=Plugin_CgProgramManager
Plugins.cfg 文g内容相当直观Q不再赘q?
Ogre::Root::showConfigDialog() ?x)有下述行?f)Q如果程序运行之间已存在一个有效的 ogre.cfg Q那么它?x)在昄对话框之前将配置文g的内容蝲入,如果用户对对话框q行?jin)操行,改变的配|参敎ͼ showConfigDialog() ?x)根据用L(fng)新选择依次调用 Root::setRenderSystem, RenderSystem::setConfigOption and Root::saveConfig Q在 ogreWin32ConfigDialog.cpp 中实玎ͼ写自q配置对话框时可以参考)(j)Q需要注意的是配|好的参敎ͼ只是?RenderSystem::initialise or RenderSystem::reinitialise 调用之后才被ȀzR?以下为典型的 ogre.cfg 文g的内?
Render System=Direct3D9 Rendering Subsystem
[Direct3D9 Rendering Subsystem]
Allow NVPerfHUD=No
Anti aliasing=None
Floating-point mode=Fastest
Full Screen=No
Rendering Device=ATI MOBILITY RADEON X300
VSync=No
Video Mode=800 x 600 @ 32-bit colour
[OpenGL Rendering Subsystem]
Colour Depth=32
Display Frequency=N/A
FSAA=0
Full Screen=No
RTT Preferred Mode=FBO
VSync=No
Video Mode=1024 x 768
Ҏ(gu)号中为可以选择的渲染子pȝQ而第一行指Z(jin)当前的选择是哪个子pȝQ方括号下面为各子系l的可选参敎ͼ在上的例子中分别列出?Direct3D 9 ?OpenGL 子系l的可选配|参数?br />Root(const String& pluginFileName = "plugins.cfg", const String& configFileName = "ogre.cfg", const String& logFileName = "Ogre.log"); ~Root();
Root 的构造函C递了(jin)ogre.cfg,只是单的文件名保存下来Q供其它Ҏ(gu)使用Q如上面曄提到q的 Root:saveConfig(void);
1. Create the DirectInput object. You use methods of this object to enumerate devices and create DirectInput device objects.
2. Enumerate devices. This is not an essential step if you intend to use only the system mouse or keyboard. To ascertain what other input devices are available on the user's system, have DirectInput enumerate them. Each time DirectInput finds a device that matches the criteria you set, it gives you the opportunity to examine the device's capabilities. It also retrieves a unique identifier that you can use to create a DirectInput device object representing the device.
3. Create a DirectInputDevice object for each device you want to use. To do this, you need the unique identifier retrieved during enumeration. For the system mouse or keyboard, you can use a standard GUID.
4. Set up the device. For each device, first set the cooperative level, which determines the way the device is shared with other applications or the system. You must also set the data format used for identifying device objects, such as buttons and axes, within data packets. If you intend to retrieve buffered data—that is, events rather than states—you also need to set a buffer size. Optionally, at this stage you can retrieve information about the device and tailor the application's behavior accordingly. You can also set properties such as the range of values returned by joystick axes.
5. Acquire the device. At this stage you tell DirectInput that you are ready to receive data from the device.
6. Retrieve data. At regular intervals, typically on each pass through the message loop or rendering loop, get either the current state of each device or a record of events that have taken place since the last retrieval. If you prefer, you can have DirectInput notify you whenever an event occurs.
7. Act on the data. The application can respond either to the state of buttons and axes or to events such as a key being pressed or released.
8. Close DirectInput. Before exiting, your application should unacquire all devices and release them, then release the DirectInput object.
?jin)解了(jin)这些基本步骤,下面我们打开OIS的源码,看看上面q?步骤装C(jin)哪里。然后我们返?ExampleFrameListener, 看看 demo 中是如何使用 OIS ?.
W一个要看的是InputManagercR他提供?jin)^台无关的接口Q负责输入系l的创徏。没啥好说的Q看源码?只列出核?j)代?Q?br />InputManager * InputManager::createInputSystem( ParamList ¶mList )
{
I(yng)nputManager* im = 0;
#elif defined OIS_WIN32_PLATFORM
im = newWin32InputManager();
#endif
im->_initialize(paramList);
return im;
}
自然Q我们只兛_(j)windowsq_下的。于是找到源码l看:
void Win32InputManager::_initialize( ParamList ¶mList )
{
//Create the device
hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&mDirectInput, NULL );
/Ok, now we have DirectInput, parse whatever extra settings were sent to us
_parseConfigSettings( paramList );
_enumerateDevices();
}
首先Q完成了(jin)8步骤中的W?步, Create the DirectInput object ?在_parseConfigSettings ( paramList ); 中对以后要创徏的设备(键盘Q鼠标等Q的属性,例如独占模式Q前台模式等。这些属性列表paramList l过处理后,之后在创备的时候传入?
void Win32InputManager::_enumerateDevices()
{ //Enumerate all attached devices
mDirectInput->EnumDevices(NULL, _DIEnumKbdCallback, this, DIEDFL_ATTACHEDONLY);
}
完成?步骤中的W? 部分 Enumerate devices Q它是针Ҏ(gu)戏杆q类设计的,对于键盘Q鼠标ƈ不需要。InputManager 创徏之后Q就可以用它来创建键盘,鼠标?jin)。它提供?jin)如下的?gu)Q?
Object * Win32InputManager::createInputObject( TypeiType, boolbufferMode )
{
Object* obj = 0;
switch( iType )
{
case OISKeyboard: obj = newWin32Keyboard( this, mDirectInput, bufferMode, kbSettings ); break;
case OISMouse: obj = newWin32Mouse( this, mDirectInput, bufferMode, mouseSettings ); break;
obj->_initialize();
return obj;
}
鼠标Q键盘都有各自的cd装。下面以键盘ZQ看看它的源码中都做?jin)些什么:(x)
void Win32Keyboard::_initialize()
{
1 mDirectInput->CreateDevice(GUID_SysKeyboard, &mKeyboard, NULL);
2 mKeyboard->SetDataFormat(&c_dfDIKeyboard)
3 HWNDhwin = ((Win32InputManager*)mCreator)->getWindowHandle();
4 mKeyboard->SetCooperativeLevel( hwin, coopSetting)))
5 mKeyboard ->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph )))
6 HRESULThr = mKeyboard->Acquire();
}
q里可是做了(jin)不少的工?看看Ҏ(gu)名就可以明白。ؓ(f)说明方便加上?jin)标受?br />1Q?它做?步骤中的W?步, Create a DirectInputDevice object for each device you want to use ?
2, 3 4,5它们共同做了(jin)8步骤中的W?步- Set up the device 。这里面包括Q设|键盘的数据格式Q协作模式,其他属性。很明显Q?做了(jin)8 步步骤中的第5步- Acquire the device ?意思是告诉DirectInput,我准备从讑֤上获取数据了(jin)Q给我做好生Z着。准备工作都做好?jin),可以获取数据了(jin),通过讑֤提供的下面这个方法来做这件事。这是8 步骤中的W?件事- Retrieve data
void Win32Keyboard::capture()
{ if( mBuffered )
_readBuffered();
else
_read();
}
从源码中我们可以看到Q根据数据的不同Q进行了(jin)不同的处理。呵Q什么不同呢Q再从MSDN上抄一D吧Q一看就明白Q?Buffered and Immediate Data
DirectInput supplies two types of data: buffered and immediate. Buffered data is a record of events that are stored until an application retrieves them. Immediate data is a snapshot of the current state of a device. You might use immediate data in an application that is concerned only with the current state of a device - for example, a flight combat simulation that responds to the current position of the joystick and the state of one or more buttons. Buffered data might be the better choice where events are more important than states - for example, in an application that responds to movement of the mouse and button clicks. You can also use both types of data, as you might, for example, if you wanted to get immediate data for joystick axes but buffered data for the buttons.
呵,清楚?jin)吧。l看代码, capture()有两个分枝,分别处理~冲数据与立x据?br />首先是缓冲数据,d代码如下Q?
void Win32Keyboard::_readBuffered()
{
DIDEVICEOBJECTDATA diBuff[KEYBOARD_DX_BUFFERSIZE];
DWORD entries = KEYBOARD_DX_BUFFERSIZE;
mKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );
//Update keyboard and modifier states.. And, if listener, fire events
for(unsignedinti = 0; i < entries; ++i )
{
KeyCode kc = (KeyCode)diBuff[ i ].dwOfs;
if( diBuff[ i ].dwData & 0x80 )
{
if( listener )
istener->keyPressed( KeyEvent( this,kc,_translateText(kc) ) );
}
else
{
//Fire off event
if( listener )
listener->keyReleased( KeyEvent( this, kc, 0 ) );
}
}
}
q段代码中,GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );取得?jin)缓冲数?d?jin)一个数l:(x)寸是EYBOARD_DX_BUFFERSIZE,可以看到entries也等于此倹{Entries的作用除?jin)开始调用时说明数组的大,同时在函数调用返回时说明数组里有多少个数据填入(没有_多的~冲数据Q当然填不满啦,假如entriesq回6Q说明还?个按键信息在~冲里等待处理)(j)。很明显Qfor循环是对这些未处理的信息进行处理。KeyCode kc = (KeyCode)diBuff[ i ].dwOfs; q是看看Q这个事件是由哪个键引走.KeyCode是个枚D型:(x)
enum KeyCode
{
C_1 = 0x02,
KC_2 = 0x03,
KC_3 = 0x04,
KC_A = 0x1E,
KC_S = 0x1F,
KC_D = 0x20,
}
定?jin)是哪个键,接下来就要问了(jin),是按下还是释放?br /> if ( diBuff[ i ].dwData & 0x80 ) 回答?jin)这个问?下面做的?
if ( listener )
istener->keyPressed( KeyEvent( this,kc,_translateText(kc) ) );
它把相关的信息内容包装成KeyEventQ作为参数发l了(jin)Q已l注册了(jin)的侦听?可能不太明白Q解释一下:(x)当我们按下了(jin)某个键,可以认ؓ(f)是发生了(jin)个KeyEvent,E序里会(x)响应q个事gQ方法就是把某个cd例注册ؓ(f)侦听?listener),说白?jin)就是告诉OIS,当键盘按下释攄时候你告诉我啊Q我要响应他。这里OIS(g)到按键事g发生?jin),?gu)侦听者的hQ调用侦听者的Ҏ(gu)(keyPressed)。也许有疑问QOIS 怎么知道侦听者实C(jin)keypressedQ)(j)Ҏ(gu)呢?不急先看以下代码,在GUI demo里:(x)
class GuiFrameListener : publicExampleFrameListener, publicOIS::KeyListener, publicOIS::MouseListener
看到?jin)吧Q它l承自?OIS:KeyListener ,从OIS的源码中扑ֈ它:(x)
class _OISExport KeyListener
{
public:
virtual ~KeyListener() {}
virtual bool keyPressed( constKeyEvent &arg ) = 0;
virtual bool keyReleased( constKeyEvent &arg ) = 0;
};
哈哈Q他正是定义?jin)这两个接口Q既然承自他,当然也就拥有?jin)这两个接口Q于是GuiFrameListener成了(jin)合理合法的Listener?jin)?
哦,它在哪里注册的呢Q很?在GuiFrameListener的构造函数里我们看到Q?
{
mMouse ->setEventCallback(this);
mKeyboard->setEventCallback(this);
}
l箋挖源码:(x)
virtual void Keyboard : QsetEventCallback ( KeyListener *keyListener ) {listener=keyListener;}
q下应该明白了(jin)吧。很明显GUI Demo里用了(jin)~冲模式.
剩下来就是立x式的数据?jin),他很好理解?x)
void Win32Keyboard::_read()
{
mKeyboard->GetDeviceState( sizeof(KeyBuffer), &KeyBuffer );
}
它把键盘当下的状态保存到~冲中去Q要想知道哪个键是否按下Q只要对照缓农y(c)按囄(ch)骥”就可以?jin)?br /> bool Win32Keyboard::isKeyDown( KeyCodekey )
{
return (KeyBuffer[key] & 0x80) != 0;
}
q个键按下了(jin)吗?那个键按下了(jin)吗?那那个呢Q呵Q真啰嗦Q得一个个的问?
于是我们 GUI Demo 中可以看C列的代码Q?
virtual bool processUnbufferedKeyInput(const FrameEvent& evt)
{
if(mKeyboard->isKeyDown(KC_A))
mTranslateVector.x = -mMoveScale; // Move camera left
if(mKeyboard->isKeyDown(KC_D))
mTranslateVector.x = mMoveScale; // Move camera RIGHT
if(mKeyboard->isKeyDown(KC_UP) || mKeyboard->isKeyDown(KC_W) )
mTranslateVector.z = -mMoveScale; // Move camera forward
if(mKeyboard->isKeyDown(KC_DOWN) || mKeyboard->isKeyDown(KC_S) )
mTranslateVector.z = mMoveScale; // Move camera backward
?
我们用键盘要不是~冲模式Q要么立x式,不可能一起上Q这所有在 demo 中都能看刎ͼ demo 的作者懒得多写代?( 嘿嘿 ) Q它l承?ExampleFrameListener 的许多功能,?ExampleFrameListener 也实C(jin)些立x式的按键处理?Demo 没有用到。写?jin)这么多了(jin),q得 8 大步骤?Q?该第 7 步了(jin)吧:(x) ―?Act on the data Q?q有什么好说的呢?爱咋咋地Q最后一步,W?8 步――天龙八?! 呵错?jin),?Close DirectInput 其实很简单, OIS 把他们封装在各个cȝ析构函数里了(jin)Q想当然Q源码也不用贴了(jin) . 说了(jin) OIS 如何?DirectInput 装hQ参?8 大步骤,如何使用 OIS 也基本很明白?jin)。OIS Q就学到q里Q接下来该主角上?CEGUI 得休息了(jin)Q打字,资料,看代码,很篏?jin)?
下面直接涉及(qing)到相关类
首先看一?/span> ExampleFrameListener, 下面是类间关pd
ExampleFrameListener l承?FrameListener, 因此拥有?jin)它的两个方?
virtual bool frameStarted(constFrameEvent& evt)
virtual bool frameEnded(constFrameEvent& evt)
?ExampleApplication 中将它们注册?ogre 中,在适当的时候, ogre ?x)调用这两个?gu) . 下面是注册地点与时机
virtual void ExampleApplication::createFrameListener(void)
{
mFrameListener= newExampleFrameListener(mWindow, mCamera);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
createFrameListener ?ExampleApplication::setup() 中被调用.
ExampleFrameListener 又承了(jin) WindowEventListener.
WindowEventListerner 在ogre 1.4中是新加的。按照手册:(x)它是一?Callback class used to send out window events to client app. 它定义以下四个接?而显然例子程序中只用C(jin)两个
virtual void windowMoved(RenderWindow* rw) {}
virtual void windowResized(RenderWindow* rw) {}
virtual void windowClosed(RenderWindow* rw) {}
virtual void windowFocusChange(RenderWindow* rw) {}
q种所谓的回调cd何实玎ͼ下面只说明windows操作pȝ的情c(din)?
我们知道Qwindows操作pȝ监视pȝ中发生的一切,通过消息的方式通知相应的窗口。每个窗口类注册的时候,都指明一个回调过E,在那里处理传来的消息。应用程序又有各自的消息队列Q从消息队列中取得消息,然后分开l各H口.
不防从ogre的源码中看看上述windows基本q程如何实现Q这有助理解回调cȝ实现q程Q?
首先Qogre可以为我们创Z个窗口:(x)
mWindow = mRoot->initialise(true);//
于是我们q入到initialise()中看看吧Q它倒底做了(jin)些什么事情?
RenderWindow * Root::initialise(boolautoCreateWindow, constString& windowTitle)
{
// ……
mAutoWindow = mActiveRenderer->initialise(autoCreateWindow, windowTitle);
// q里QRenderSystem* mActiveRenderer
// ……
}
mActivaRenderer 只是接口Q不用实际的事情Q实际的工作由它的子cd成,现只看DirectX实现:
RenderWindow * D3D9RenderSystem::initialise( boolautoCreateWindow, constString& windowTitle )
{
// ……………………
autoWindow = this->createRenderWindow( windowTitle, width, height,
fullScreen, &miscParams );
// ………………… ..
}
好,l箋下去,看看createRenderWindow做了(jin)些什?
RenderWindow * D3D9RenderSystem::createRenderWindow(constString &name,
unsigned int width, unsignedintheight, boolfullScreen,
const NameValuePairList *miscParams)
{
// …………… ..
RenderWindow * win = newD3D9RenderWindow(mhInstance, mActiveD3DDriver,
mPrimaryWindow ? mpD3DDevice : 0);
win ->create( name, width, height, fullScreen, miscParams);
// ………………
}
l箋
void D3D9RenderWindow::create(constString& name, unsignedintwidth, unsignedintheight,
bool fullScreen, constNameValuePairList *miscParams)
{
// Register the window class
// NB allow 4 bytes of window data for D3D9RenderWindow pointer
WNDCLASS wc = { 0, WindowEventUtilities::_WndProc, 0, 0, hInst,
LoadIcon(0, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
(HBRUSH)GetStockObject(BLACK_BRUSH), 0, "OgreD3D9Wnd" };
RegisterClass(&wc);
// 定义?jin)窗口类Qƈ且进行注册,需要注意的是,你看看它把窗口类的回调函数的DZ(jin)什么?先记住,一?x)儿讨论它吧?
// Create our main window
// Pass pointer to self
mIsExternal = false;
mHWnd = CreateWindow("OgreD3D9Wnd", title.c_str(), dwStyle,
mLeft, mTop, mWidth, mHeight, parentHWnd, 0, hInst, this);
// 调用win32API把窗口真正Create出来?
WindowEventUtilities ::_addRenderWindow(this);
// q一步也要注意到
}
l箋
void WindowEventUtilities::_addRenderWindow(RenderWindow* window)
{
_msWindows.push_back(window);
}
_msWindows 定义为:(x)
typedef std::vector<RenderWindow*> Windows;
static Windows _msWindows;
Q没什么,只是个stl容器Q把生成的窗口放q去?
到现在,容器cL册好?jin),回调函数也指定?jin)Q窗口也创徏出来?jin)。现在我们l思考我们最最初的问题Q?WindowEventListerner q个回调cL如何实现所谓的回调机制的,也就是说windowEventListerner不是定义?jin)四个接口,它响应特定的windows事gQ我们要讨论的就是这四个接口Ҏ(gu)如何被调用v来,实现所谓的回调机制?q记得注册窗口类的回调函数是什么吧Q?
WindowEventUtilities::_WndProc Q?呵呵Q只要在那里调用上述四个接口函数好?jin)?
哪问题又来了(jin)Q_WndProc到哪里找q四个接口函数呢Q?
?ExampleFrameListener cȝ构造函数里我们可以看到如下代码
//Register as a Window listener
WindowEventUtilities::addWindowEventListener(mWindow, this);
啊,q就是奥U所?到源码中看看吧?
void WindowEventUtilities::addWindowEventListener( RenderWindow* window, WindowEventListener* listener )
{
_msListeners.inser t(std::make_pair(window, listener));
}
-msListeners 被定义成q样子:(x)
typedef std::multimap<RenderWindow*, WindowEventListener*> WindowEventListeners;
static WindowEventListeners _msListeners;
没有什么,是STL容器Q它使得H口(RenderWindow)?WindowEventListener)
l成?jin)亲密派对,H口有了(jin)什么消息,可以用对应的回调类响应?
只剩下一点秘密了(jin)Q到底怎么调用h的呢Q某某某说过Q源码之下,?jin)无U密Q那q源码吧:(x)
LRESULT CALLBACK WindowEventUtilities::_WndProc(HWNDhWnd, UINTuMsg, WPARAMwParam, LPARAMlParam)
{
WindowEventListeners ::iteratorstart = _msListeners.lower_bound(win),
end = _msListeners.upper_bound(win);
// 为遍历做准备
switch( uMsg ) // 消息真的来了(jin)
{
case WM_MOVE:
//log->logMessage("WM_MOVE");
win->windowMovedOrResized();
for( ; start != end; ++start )
(start->second)->windowMoved(win);
break;
case WM_SIZE:
//log->logMessage("WM_SIZE");
win->windowMovedOrResized();
for( ; start != end; ++start )
(start->second)->windowResized(win);
break;
}
}
pP四个接口函数被调用v来了(jin)Q?
也许Q也许,关于消息q有些要说明的。消息如何܇出来的?
当看C(jin)如下代码Q突然就x个朋友,?x)?j)一W?
void WindowEventUtilities::messagePump()
{
MSG msg;
while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
打破铁锅问到底吧Q它又如何被调用hQ?
void Root::startRendering(void)
{
while( !mQueuedEnd )
{
//Pump messages in all registered RenderWindow windows
WindowEventUtilities::messagePump();
if (!renderOneFrame())
break;
}
}
是q里?jin)。写的ؕ七八p,脑子也ؕ七八p,梳理一下吧?
首先QmWindow = mRoot->initialise(true);初始化,随便通过一pȝq锁反应create出来一个窗口,q把它的回调函数讑֮?WindowEventUtilities::_WndProc.
ExampleFrameListener l承?WindowEventListener 的四个约定接口函数。ƈ且在它的构造函数里调用 WindowEventUtilities::addWindowEventListener(mWindow, this); q行?jin)注册,以便特定H口消息发生时进行响? Root ::startRendering Q)(j)q入?jin)渲染@环,渲染每一帧前Q检widnows消息Qƈq行分发。于是消息进入到H口cȝ回调函数_WndProc,在函数内部根据消息的不同Q分别调用已注册的侦听器的约定接口函数?br>先写到这里吧Q原来只是想写写C(j)EGUI的用,谁知已经写了(jin)q么多了(jin)QCEGUIq一字未提。脑子ؕ?jin),先休息吧。下面还得说说OIS,CEGUI又得靠后?
if (ImmIsIME(GetKeyboardLayout(0)))
{
CEGUI::DbcsSupport::injectChar(Key);
}
else
{
CEGUI::System->injectChar((CEGUI::utf32)Key);
}
ImmIsIME(GetKeyboardLayout(0))目的是用于检则现在的输入法是否打开的。如果是在输英文状态我想你不会(x)画蛇添的。用这个需要imm32.lib的支持。MSDN上面也有说明?br />5:最后就是添加一份代码到你的CEGUI使用目里,如下Q?br />namespace CEGUI{
bool DbcsSupport::injectChar(utf32 code_point )
{
#ifndef UNICODE
static char s_tempChar[3] = "";
static wchar_t s_tempWchar[2] = L"";
static bool s_flag = false;
unsigned char uch = (unsigned char)code_point;
if( uch >= 0xA1 )
{
if( !s_flag )
{
s_tempChar[0] = (char)uch; //W一个字?br /> s_flag = true;
return true;
}
else if( uch >= 0xA1 )
{
s_tempChar[1] = (char)uch; //W二个字?br /> s_flag = false;
MultiByteToWideChar( 0, 0, s_tempChar, 2, s_tempWchar, 1); //转成宽字?br /> s_tempWchar[1] = L'\0';
utf32 code = (utf32)s_tempWchar[0];
//Font* fnt = System::getSingleton().getDefaultFont();
return CEGUI::System::getSingleton().injectChar( code );
}
else
{
return CEGUI::System::getSingleton().injectChar(code_point);
}
}
else
{
s_flag = false;
return CEGUI::System::getSingleton().injectChar(code_point);
}
#else
return CEGUI::System::getSingleton().injectChar(code_point );
#endif
}
}
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1509217
EventClicked :: pȝ预定义的事g . subscribeEvent:: 注册函数Q它事g与响应函数联接在一赗?
?ogre E序中,当侦听器收到键盘Q鼠标消息时Q首先经q适当的{换( CEGUI 可以识别Q再传递给 CEGUI 。下面这个函数执行鼠标键标识转换?
CEGUI::MouseButton convertOgreButtonToCegui(int buttonID)
{
switch (buttonID)
{
case MouseEvent::BUTTON0_MASK:
return CEGUI::LeftButton;
case MouseEvent::BUTTON1_MASK:
return CEGUI::RightButton;
case MouseEvent::BUTTON2_MASK:
return CEGUI::MiddleButton;
case MouseEvent::BUTTON3_MASK:
return CEGUI::X1Button;
default:
return CEGUI::LeftButton;
}
}
?CEGUI 需要知道的键盘Q鼠标消息告知它。即?OGRE 处理q些消息旉知 CEGUI 。以下这函数说明?jin)用法?
void mouseMoved (MouseEvent *e)// 鼠标Ud
{
CEGUI::System::getSingleton().injectMouseMove(
e->getRelX() * mGUIRenderer->getWidth(),
e->getRelY() * mGUIRenderer->getHeight());
e->consume();
}
void mousePressed (MouseEvent *e)// 鼠标按下
{
CEGUI::System::getSingleton().injectMouseButtonDown(
convertOgreButtonToCegui(e->getButtonID()));
e->consume();
}
void mouseReleased (MouseEvent *e)// 鼠标弹v
{
CEGUI::System::getSingleton().injectMouseButtonUp(
convertOgreButtonToCegui(e->getButtonID()));
e->consume();
}
void keyPressed(KeyEvent* e)// 键按?
{
CEGUI::System::getSingleton().injectKeyDown(e->getKey());
CEGUI::System::getSingleton().injectChar(e->getKeyChar());
e->consume();
}
void keyReleased(KeyEvent* e)// 键弹?
{
CEGUI::System::getSingleton().injectKeyUp(e->getKey());
e->consume();
}
?CEGUI 中用中文的问题Q ?现在ȝ?jin)一下在 CEGUI 中显CZ文需要注意的事项 :
1 、将 simhei.ttf copy to \ogrenew\Samples\Media\gui
2 、将 simhei-12.font 拷到上目录内容ؓ(f)
<?xml version="1.0" ?>
<Font Name="SimHei-12" Filename="simhei.ttf" Type="Dynamic" Size="12" NativeHorzRes="800" NativeVertRes="600"
AutoScaled="true">
<GlyphSet Glyphs=" 你好世界退出演C渲染到新材质徏~辑H口 " /> Q?<---- 自己要用到的汉字Q?
</Font>
注意大小写!Q?GlyphSet Glyphs 是在E序中要用到的汉字,它是?cegui 预生成一个字W图像集用的Q想当然?:-P Q如果修改了(jin)q个文gQ注意要?Unicode Q?UTF-8 Q的~码来保存,?vc7.1 中:(x)文g -> 高保存选项 的 ?~码 栏中选择?
3 、在 TaharezLook.scheme ?
<Font Name="Tahoma-12" Filename="tahoma-12.font" /> 后加?
<Font Name="SimHei-12" Filename="simhei-12.font" /> 注意大小?
以上是一些准备工?
4 、在自己的应用中讄默认字体
mGUISystem->setDefaultFont((CEGUI::utf8*)"Tahoma-12"); 改ؓ(f)
mGUISystem->setDefaultFont((CEGUI::utf8*)"SimHei-12");
5 、在自己的应用程序中可以把相关?Text 属性该Z文了(jin)Q如Q?
item = new CEGUI::ListboxTextItem((CEGUI::utf8*)" 退?", 6);
同样要注意的是要保存?Unicode Q?UTF-8 Q的~码。同时这些字要是?simhei-12.font 中定义过的字Q当然也可以象那?CEGUIChieseDemo
那样用动态生成如Q?
gfont->defineFontGlyphs(gfont->getAvailableGlyphs() + (utf8*)" 当前最?jng)_q_的框架率三角 ");
~译自己的程序,应该可以看C文了(jin)Q罗嗦一下,C只要有汉字出现的文g׃存ؓ(f) Unicode Q?UTF-8 Q编码的Q!Q?