gogoplayer
Ogre,MySQL&&MySQL++,光线跟踪
posts - 15,comments - 23,trackbacks - 0

   这个月接到数据库服务器设计任务,网络部分的IOCP已经封装完成,剩下的是数据库设计部分,原先数据库部分API采用的是MySQL C的,C的代码虽然也简单,但是会有些混乱,没有MySQL++封装后好用,昨天看MySQL C的代码的时候感觉C已经比较好用了,但是这一切在我使用了MySQL++后彻底改变了,MySQL++基于MySQL之上,对C的API进行了再一次的封装,通过这次封装,屏蔽了原先C代码的很多细节,而且加上了异常机制,例如数据库连接、事务、结果集等对象概念突出了,还有他的SSQLS功能,太好用了,它就像SQL模板,用来生成各种SQL语句。
   由于是数据库服务器设计,必然涉及到多线程操作,而且不仅是应用程序的多线程,数据库操作也要支持多线程,设想如果一个数据库操作导致阻塞导致,那么面对游戏的数据库服务器,将是海量的数据库操作,这样的延时那可就不得了了。先来说说应用程序的多线程操作,是这样的,目前的设计是多个登陆服务器链接一个数据库服务器,通过登陆服务器来提交数据库操作请求,一个登陆服务器可以有很多玩家连在那里,玩家通过帐号id来唯一标识,这样一说脉络就清晰了。玩家通过登陆服务器(其实玩家最初还要进过网关服务器中转,这里掠过)提交不同的数据库操作,数据库服务器接受到数据库操作消息就把这个消息放到对应的线程去操作,这个线程也不是随机找一个空闲线程操作就可以的,因为涉及到网络同步的复杂问题,我们必须保证同一个玩家提出多个的数据库操作依次执行,也就是队列,不能出现玩家的第二个数据库操作完成,而第一个却还没动,原因应很简单,玩家的操作是有前后连续性的,顺序错误有时候会带来灾难性的后果,要知道数据库可是存放着玩家的资料,诸如玩家等级,装备,攻防参数等重要信息,如果这些数据出错,我想你应该知道这个利害了吧。
   废话少说,先来看下数据库多线程简单图示:

   上图是应用程序多线程示例,每个线程有个数据库操作队列,不同玩家的数据库操作会通过帐号id被放到相应的线程中,至于这其中的同步互斥,无非就是用用互斥量,临界区控制一下,没什么难度。每个线程采用事件机制激活,当有来自网络的数据被解析为一个数据库操作并被投递到一个数据库操作队列(DBOperationQueue)后,激活事件,线程原先是在等待事件,现在被激活了,自然就运行了,开始解析操作,每个线程内部会创建一个数据库连接,对应于MySQL++,就是一个mysqlpp::Connection,其实在mysqlpp::Connection的内部,会自动调用mysql_thread_init()这个MySQL的C API,这样原本我以为c++的封装和C就对等了,但是搜索MySQL++源代码发现他并没有调用mysql_thread_end(),这就有问题了,没有调用这个函数将不会析构一些数据库线程内的数据,造成内存泄漏,带着这个疑问再找下去,发现了mysqlpp::Connection自带lock机制,也就是同一个线程对他的操作将会被自动互斥,没有调用mysql_thread_end(),只是mysqlpp::Connection没有使用MySQL线程相关的那部分代码,自己实现了lock机制,实际上是一回事。

   上面的图示显示的全是数据库操作线程,实际上还有一个网络消息处理线程,这个线程有IOCP管理,因为DBServer继承自IOCP,大致的流程就是这样,网络消息处理线程解析消息,并把数据库操作放到相应的数据库操作队列,每个数据库操作线程只处理对应的队列,数据库线程可以开多个,我们的工作中开了10几个,至此,只要妥善处理好他们的同步互斥关系,数据库服务器就会稳定的的运行,虽说同步互斥不难,但是数据库服务器的设计充斥着大量的同步互斥操作,稍不注意,有可能就调入死锁、数据异常陷阱中,多多留心啊,^_^。

posted @ 2007-07-18 23:12 gogoplayer 阅读(710) | 评论 (4)编辑 收藏
杂七杂八的事情终于结束了,可以专心研究GPU了,在公司和azure交流中了解到GPU精粹2已经出版,下午去了书店,只有三本了,很热销阿,翻开书,有彩页,爽啊,不过价格也很爽,要128大洋,破财了,接下来几天啃包子好了。相对于第一版,第二版书厚了一些,越厚越好啊,大致看了一下,越翻越有看头,有福啦,^_^
posted @ 2007-07-15 14:30 gogoplayer 阅读(742) | 评论 (1)编辑 收藏

   这几日折腾了一下Ogre的文件系统,我总想把它的文件系统从它的庞大代码堆中分理出来,这样我就可以重复利用这一部分代码,^_^,经过一翻测试,Ogre的文件系统完全被我分理处来,而且非常好用,我把他加入到我原来的2D项目中,工作非常顺利,Ogre抽象的他好了,以至于我几乎不用怎么担心他的兼容性,下面就简要介绍一下分离方法。
   首先,我要说得是:我所分离的部分只是文件管理部分,不包括Ogre的资源相关部分,涉及到Ogre的资源部分不属于本文探讨范围,其次,Ogre文件系统相关知识可以从网上下载《Ogre分析之文件系统》仔细研究,Mythma的文档给了我很大的帮助,再次一并感谢。
   Ogre文件系统的核心是Archive和DataStream,从Ogre继承结构可以看出,他们都是抽象基类,Ogre自带了Ogre::FileSystemArchive和Ogre::ZipArchive,分别用于实现文件夹和压缩包,如果你要实现自己的文件格式,比如自己的数据包,一个包里有多个文件,只需写个类似ZipArchive的类,然后向ArchiveManager注册即可,ArchiveManager是文件管理类。DataStream提供了操纵文件的统一接口,我在下面使用了size()和read(),他有四个派生类。
   在这次操作种,找到OgreArchive.h,OgreArchiveFactory.h,OgreArchiveManager.h,OgreZip.h及其他们对应的实现文件(如果有),把他们复制出来,去掉这些头文件中关于资源管理的信息,当然,还有其他相关文件需要提取,例如Log方面的,智能指针也是,还有Singleton等,这些在我上次试验中已经提取完毕,这里不再重复,这篇文章只是那篇文章的补充,测试代码判断如下,实现了用Ogre接口从文件夹和Zip读取数据给DX原始API使用。

 ArchiveFactory *mFileSystemArchiveFactory = new FileSystemArchiveFactory();
 ArchiveManager::getSingleton().addArchiveFactory( mFileSystemArchiveFactory );
 FileSystemArchive *pFileSystem = static_cast<FileSystemArchive *>(m_ArchiveMgr.load("../Media", "FileSystem"));
 StringVectorPtr pStrVectorFileSystem = pFileSystem->list(true);

 ArchiveFactory *mZipArchiveFactory = new ZipArchiveFactory();
 ArchiveManager::getSingleton().addArchiveFactory( mZipArchiveFactory );
 ZipArchive *pZip = static_cast<ZipArchive *>(m_ArchiveMgr.load("../Media/Package/OgreCore.zip", "Zip"));
 StringVectorPtr pStrVectorZip = pZip->list(true);

 /* 从文件中读取数据
 DataStreamPtr pDataStream = pFileSystem->open("Texture/mm.jpg");
 size_t s = pDataStream.getPointer()->size();
 char *pBuf = new char[s];
 pDataStream.getPointer()->read(static_cast<void *>(pBuf), s);
*/

//从Zip读取数据
 DataStreamPtr pDataStream = pZip->open("OgreCore/ogretext.png");
 size_t s = pDataStream.getPointer()->size();
 char *pBuf = new char[s];
 pDataStream.getPointer()->read(static_cast<void *>(pBuf), s);

//自定义类,实际是调用D3DXCreateTextureFromFileInMemoryEx
 CScene9::CreateGraph2D(&graph);
 graph.LoadFromMemory(pBuf, s);
   

posted @ 2006-12-05 21:57 gogoplayer 阅读(648) | 评论 (0)编辑 收藏

总的来说,非常的好用,Ogre在即将推出的1.4版本中将完全去处原来破破烂烂的输入部分,取而代之得是OIS(Object-oriented Input Library),对我来说,Ogre去除自带的输入系统是个非常好的消息,这本来就不是一个图形渲染系统该有的,OIS出现的太及时了,OIS的作者是Ogre的MVP,我看过他的代码,功力深厚,虽然现在OIS功能还不是很高,但是比原来的那个还是要好的多,提供了立即模式和缓冲模式(可共用),也就是设计模式那一套路,用个监听器得到消息,这样的封装用起来非常简单。
class EventHandler : public KeyListener, public MouseListener, public JoyStickListener
{
public:
 EventHandler() {}
 ~EventHandler() {}
 bool keyPressed( const KeyEvent &arg ) {
  std::cout << "\nKeyPressed {" << arg.key
   << ", " << ((Keyboard*)(arg.device))->getAsString(arg.key)
   << "} || Character (" << (char)arg.text << ")" << std::endl;
  return true;
 }
 bool keyReleased( const KeyEvent &arg ) {
  if( arg.key == KC_ESCAPE || arg.key == KC_Q )
   appRunning = false;
  return true;
 }
 bool mouseMoved( const MouseEvent &arg ) {
  const OIS::MouseState& s = arg.state;
  std::cout << "\nMouseMoved: Abs("
      << s.abX << ", " << s.abY << ", " << s.abZ << ") Rel("
      << s.relX << ", " << s.relY << ", " << s.relZ << ")";
  return true;
 }
 bool mousePressed( const MouseEvent &arg, MouseButtonID id ) {
  std::cout << "\nMousePressed: " << id << " time[" << arg.timeStamp << "]";
  return true;
 }
 bool mouseReleased( const MouseEvent &arg, MouseButtonID id ) {
  std::cout << "\nMouseReleased: " << id << " time[" << arg.timeStamp << "]";
  return true;
 }
 bool buttonPressed( const JoyStickEvent &arg, int button ) {
  std::cout << "\nJoy ButtonPressed: " << button << " time[" << arg.timeStamp << "]";
  return true;
 }
 bool buttonReleased( const JoyStickEvent &arg, int button ) {
  return true;
 }
 bool axisMoved( const JoyStickEvent &arg, int axis )
 {
     std::cout << "\nJoy Axis " << axis << " " << arg.state.mAxes[axis].abX
                  << " " << arg.state.mAxes[axis].abY;
  return true;
 }
 bool povMoved( const JoyStickEvent &arg, int pov )
 {
  std::cout << "\nJoy POV" << pov << " ";

  if( arg.state.mPOV[pov].direction & Pov::North ) //Going up
   std::cout << "North";
  else if( arg.state.mPOV[pov].direction & Pov::South ) //Going down
   std::cout << "South";

  if( arg.state.mPOV[pov].direction & Pov::East ) //Going right
   std::cout << "East";
  else if( arg.state.mPOV[pov].direction & Pov::West ) //Going left
   std::cout << "West";

  if( arg.state.mPOV[pov].direction == Pov::Centered ) //stopped/centered out
   std::cout << "Centered";
  return true;
 }
};

上面的代码就是定义了一个集键盘、鼠标、手柄消息控制于一身的监听器,有相应事件产生就会调用相应的函数,使用及其方便,我把OIS的VC8版本接入到我原来的一个2D引擎中,工作非常顺利,这也使我彻底的淘汰了原来不良的设计,如果你有自己的引擎,你也可以试一下,OIS 下载 http://sourceforge.net/projects/wgois/ ,再来个Ogre对于他的介绍 http://www.ogre3d.org/wiki/index.php/Using_OIS 。

posted @ 2006-11-29 01:11 gogoplayer 阅读(1529) | 评论 (8)编辑 收藏

绝对轻量级的声音引擎,audiere,支持跨平台,可以去http://sourceforge.net/projects/audiere/下载,再给个小例子,^_^
用VC6或者其他配置好工程,建个控制台程序即可
#include <iostream>
using namespace std;

#include "audiere.h"

audiere::AudioDevicePtr device(audiere::OpenDevice());
audiere::OutputStreamPtr stream(audiere::OpenSound(device, "music.wav", false));

int main()
{
 stream->setRepeat(true);
 stream->setVolume(0.5f); // 50% volume
 stream->play();

 int a;
 cin >> a;

 return 0;
}

posted @ 2006-11-29 00:54 gogoplayer 阅读(728) | 评论 (1)编辑 收藏

其实就是手动配置渲染系统,去掉Ogre自带的对话框,如果会用Ogre的基本框架,下列代码很容易理解

bool RPGApp::configure(void)
{
    // Show the configuration dialog and initialise the system
    // You can skip this and use root.restoreConfig() to load configuration
    // settings if you were sure there are valid ones saved in ogre.cfg
    //if(mRoot->showConfigDialog())
   RenderSystemList *rsList = mRoot->getAvailableRenderers();
   int c=0;
   bool foundit = false;
   RenderSystem *selectedRenderSystem=0;
   while(c < (int) rsList->size()){
      selectedRenderSystem = rsList->at(c);
      String rname = selectedRenderSystem->getName();
      if(rname.compare("Direct3D9 Rendering Subsystem")==0){
         foundit=true;
         break;
      }
      c++; // <-- oh how clever
   }
   if(!foundit) return false; //we didn't find it...
   
   //we found it, we might as well use it!
   selectedRenderSystem->setConfigOption("Full Screen","Yes"); 
   selectedRenderSystem->setConfigOption("Video Mode","1024 x 768 @ 32-bit colour");
   selectedRenderSystem->setConfigOption("Allow NVPerfHUD","No");
   selectedRenderSystem->setConfigOption("Anti aliasing","None");
   selectedRenderSystem->setConfigOption("Floating-point mode","Fastest");
   //selectedRenderSystem->setConfigOption("Rendering Device","RADEON 9200");
   selectedRenderSystem->setConfigOption("VSync","No");
   mRoot->setRenderSystem(selectedRenderSystem);
   
    mWindow = mRoot->initialise(true, "Dire Desire");
   return true;
}

posted @ 2006-11-29 00:45 gogoplayer 阅读(661) | 评论 (0)编辑 收藏
      

Terrain Scene Manager参数详解

       在场景中地形的形状和染色从一张高度图,一张地形纹理和一张细节贴图中被计算出来。高度图是一张灰度图,每个象素表现一个高度值,0表示地平面,255表示地形最高点。

    地形上面覆盖的的那层纹理被拉伸。这些地形纹理具有代表性的是斑驳的褐色,绿色,白色或者灰色适合于地表,草,雪或岩石地形。地形纹理通常比要覆盖地形小的多,所以近看时通常很模糊。为了解决这个问题,使用了一张细节纹理,当近看时混合地形纹理。细节纹理没有被拉伸,消除了近看模糊问题。

    地形被分成一个地形小块表格。地形小块可能在不同的细节层次被场景管理器显示出来,依赖于小块的布局和离观察者的距离。整个地形被投射到一个世界坐标空间中,在里面地形的尺寸。

目录

Configuration Parameters

地形场景管理器通过terrain.cfg教本被设置。

Basic Configuration Parameters

1.      WorldTexture: 指定地形纹理的名称。

2.      DetailTexture: 指定细节纹理的名称。

3.      DetailTile: 这个指定细节纹理在每个地形小块中重复次数,也就是说,如果这个值是n,细节纹理将在每个地形小块的n*n格中被显示。如果这个值设置过低,地形在近看时会模糊,反之则会在放眼观察时看起来有反复模式(露馅了,^_^) 。

4.      PageSource: 指定高度图数据的来源,默认为Heightmap.

5.      Heightmap.image: 指定高度图的名称。尺寸必须是方形,而且要符合2^n+1, 高度图越大,地形细节就越高,但是程序消耗的的资源也越多(包括程序启动时间)。

6.      PageSize: 地形将会有PageSize * PageSize那么大。如上所述,必须要符合2^n+1 (如651292575131025)。

7.      TileSize: 地形小块有 TileSize * TileSize大小。这个数必须小于PageSize这个数必须要符合2^n+1须要符合2^n+1。设的过小会严重影响性能,太大的话又会在场景的某些部分导致不必要的高细节。

8.      MaxPixelError: 指定当决定哪个细节层次被使用时的最大允许误差。设置过高会导致地形撕裂,过低则会影响性能。

9.      PageWorldX, PageWorldZ: 设定地形在世界坐标系中的范围,地形越大,细节越低,地形使用的顶点数基于高度图,不是世界大小,你可以缩放地形到你想要的任意大小。

10.  MaxHeight: 在世界坐标系中地形的最大高度。高度图中的0..255 缩放到世界坐标系中的 0..MaxHeight

11.  MaxMipMapLevel: 指定渲染地形使用的细节层次的数目。地形的远处和平坦处会以低细节渲染。

Advanced Configuration Parameters

有些高度图保存为raw格式,下列参数描述raw格式。

12.  Heightmap.raw.size: 指定高度图的尺寸,要求同上。

13.  Heightmap.raw.bpp: 指定每象素字节数,1=8 bits, 2=16 bits

14.  Heightmap.flip: 如果设置为true,则翻转高度图。

 

高度图处理说明

    高度图必须是灰度图,如果不是,可以使用photoshop处理,选择菜单【图像】->模式->灰度,然后存为png格式即可。


 

坐标系问题

    Terrain Scene Manager把纹理图的左上角作为世界原点(000),以从左到右为x正方向,从上到下为z正方向,从屏幕里到屏幕外为y正方向,符合右手坐标系。

 

posted @ 2006-11-29 00:22 gogoplayer 阅读(1281) | 评论 (0)编辑 收藏
      很显然,数学函数每个人都用过,不外乎sin,cos等等,我最近对Ogre的数学函数库进行了一番测试,原以为Ogre采用汇编加速的数学函数效率会远远超越math.h提供的函数,但是经过测试,我感到很吃惊(使用VC6.0测试),在Debug模式下,采用查表法求sin,cos等函数确实比原来的库函数效率高了很多,但到了Release模式,一切都变了,Ogre用汇编写的效率远远落后于库函数,查表法也失去了原有的优势,要想获得更高的精度,就要扩大表的大小,即使这样,库函数在精度和速度上还是领先,不得不佩服VC的Release编译,最后经过了结,推测为Release编译模式使用了浮点优化,测试平台P4630+winxp+1G,欢迎探讨。
posted @ 2006-04-18 18:49 gogoplayer 阅读(249) | 评论 (1)编辑 收藏

单件设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。单件设计模式在OGRE中使用还是比较频繁的,管理日志的LogManager,还有SkeletonManager等管理器,就连Root也使用了这种设计模式。分析OGRE的使用,发现这种设计模式是很简单的,如下:
    template <typename T> class Singleton
    {
    protected:

        static T* ms_Singleton;

    public:
        Singleton( void )
        {
            assert( !ms_Singleton );
           ms_Singleton = static_cast< T* >( this );
        }
        ~Singleton( void )
            {  assert( ms_Singleton );  ms_Singleton = 0;  }
        static T& getSingleton( void )
  { assert( ms_Singleton );  return ( *ms_Singleton ); }
        static T* getSingletonPtr( void )
  { return ms_Singleton; }
    };
}

接下来只要继承这个模板类并且重载getSingleton(),getSingletonPtr(),确实很方便,不是么^_^
STL就更不用说了,没有STL,OGRE恐怕就不是现在这个样子,说到这里,我对OGRE采用的STL命名法并不赞同,举个例子:
  typedef std::map<String, Log*, std::less<String> > LogList;   
这是一个map,却命名为LogList,STL内含list容器,很容易混淆,混淆的还在后面
  typedef std::vector<LogListener*>     LogListenerList;
明明是个vector,却命名为LogListenerList,如果整个工程统一使用这一风格,还可以接受,但下面这句就违反了
    typedef std::vector<String> StringVector;
这里使用了vector命名StringVector,我个人对这种命名比较认可,一目了然.

posted @ 2006-04-09 23:42 gogoplayer 阅读(422) | 评论 (1)编辑 收藏
Sphere类中intersects写成了interects,还有文档中也有大量笔误,benefit写成benifit,还好我发现的这些笔误无伤大雅。我最近在研究OGRE稳定的头文件,OgreStableHeaders.h,OGRE确实是个庞大的系统,不枉费我从众多引擎中选他作为研究对象,其中使用的一些设计模式却有独具匠心之处,最近学校发了笔小钱,用来打印OGRE,这样可以随时拿出来看了,好Happy啊,^_^。
posted @ 2006-04-08 09:49 gogoplayer 阅读(634) | 评论 (5)编辑 收藏
仅列出标题  下一页