http://blog.csdn.net/sxy0082002/archive/2009/06/30/4308340.aspx
前面写过了dtCore::Base的一些学习体会,现在往下说说dtCore::System。在接触过的几个仿真引擎中(貌似只有vp和Delta3d)都使用了这个作为高级帧控制手段。如果要想将引擎开发出来的项目塞到MFC这类东东中也就需要自己手动来使用它了。其实应该是重新dtABC::Application 并在里面调用System的函数。那么了解它也就变得很重要了。尤其对我这种入门级菜鸟来说。
由于里面的内容比较多,我准备将其分为两个部分来说:
1 它是如何控制帧的 根据自身状态发送消息
2 它是根据什么来控制帧的 系统时间,自身状态 , 真实时间 模拟时间
先列出该类中一部分函数和内置变量吧
view plaincopy to clipboardprint?
class DT_CORE_EXPORT System : public Base
{
public:
DECLARE_MANAGEMENT_LAYER(System)
public:
static System& GetInstance();
static void Destroy();
void Start();
void Step();
void StepWindow();
void Stop();
void Run();
private:
System();
static System* mSystem;
static bool mInstanceFlag;
SystemStageFlags mSystemStages;
bool mRunning;
bool mShutdownOnWindowClose;
bool mPaused;
bool mWasPaused;
void InitVars();
void CameraFrame();
void SystemStep();
};
class DT_CORE_EXPORT System : public Base
{
public:
DECLARE_MANAGEMENT_LAYER(System)
public:
static System& GetInstance();
static void Destroy();
void Start();
void Step();
void StepWindow();
void Stop();
void Run();
private:
System();
static System* mSystem;
static bool mInstanceFlag;
SystemStageFlags mSystemStages;
bool mRunning;
bool mShutdownOnWindowClose;
bool mPaused;
bool mWasPaused;
void InitVars();
void CameraFrame();
void SystemStep();
};
先看下System的构造函数吧。这里其构造函数是私有的,也就是说你没法建立实例,其实不然,就像很多这种核心部分的类一样,它提供了一个GetInstance()来获取System的引用。
System& System::GetInstance()
{
if (!mInstanceFlag)
{
mSystem = new System();
mSystem->SetName("System");
mInstanceFlag = true;
}
return *mSystem;
}
其中mSystem是一个静态指针,这样就可以保证一个应用程序中只含有一个System实体,并且像《Effective c++》中说的,通过静态函数中创建的静态变量是顺序一定的。想要销毁System时可以直接调用静态函数Destroy()来完成。
已经知道了如何获取一个System实体后,我们来看一下他到底是如何根据所设置的控制状态标识符控制帧的。
view plaincopy to clipboardprint?
enum SystemStages
{
STAGE_NONE = 0x00000000,
STAGE_EVENT_TRAVERSAL = 0x00000001,
STAGE_POST_EVENT_TRAVERSAL = 0x00000002,
STAGE_PREFRAME = 0x00000004,
STAGE_CAMERA_SYNCH = 0x00000008,
STAGE_FRAME_SYNCH = 0x00000010,
STAGE_FRAME = 0x00000020,
STAGE_POSTFRAME = 0x00000040,
STAGE_CONFIG = 0X00000080,
STAGES_DEFAULT = STAGE_EVENT_TRAVERSAL|STAGE_POST_EVENT_TRAVERSAL|STAGE_CAMERA_SYNCH|STAGE_FRAME_SYNCH|STAGE_PREFRAME|STAGE_FRAME|STAGE_POSTFRAME|STAGE_CONFIG
};
enum SystemStages
{
STAGE_NONE = 0x00000000,
STAGE_EVENT_TRAVERSAL = 0x00000001,
STAGE_POST_EVENT_TRAVERSAL = 0x00000002,
STAGE_PREFRAME = 0x00000004,
STAGE_CAMERA_SYNCH = 0x00000008,
STAGE_FRAME_SYNCH = 0x00000010,
STAGE_FRAME = 0x00000020,
STAGE_POSTFRAME = 0x00000040,
STAGE_CONFIG = 0X00000080,
STAGES_DEFAULT = STAGE_EVENT_TRAVERSAL|STAGE_POST_EVENT_TRAVERSAL|STAGE_CAMERA_SYNCH|STAGE_FRAME_SYNCH|STAGE_PREFRAME|STAGE_FRAME|STAGE_POSTFRAME|STAGE_CONFIG
};
可以通过以下方式来开启和关闭System所发送的特定控制消息。
view plaincopy to clipboardprint?
dtCore::System::GetInstance().SetSystemStages( System::STAGE_PREFRAME|System::STAGE_FRAME )
dtCore::System::GetInstance().SetSystemStages( System::STAGE_PREFRAME|System::STAGE_FRAME )
上述每个状态都对应一个消息。通过控制当前状态来控制该消息是否被发出。
例如:如果SetSystemStages中没有设置STAGE_EVENT_TRAVERSAL的话,则不会发出
MESSAGE_EVENT_TRAVERSAL消息,那么也就不会有输入事件,窗口事件被监听到。
System类根据SystemStages判断是否向app类发送消息。关于所发送消息的部分说明如下所示:
view plaincopy to clipboardprint?
/*这个消息是用来响应当前帧下的dtABC::Application相应OSG事件。用户不要监听这个事件*/
const static dtUtil::RefString MESSAGE_EVENT_TRAVERSAL;
/*这个消息是在MESSAGE_EVENT_TRAVERSAL后立即发送出去的。可以通过监听这个消息来确定当前帧下前输入或者窗口事件以完成*/
const static dtUtil::RefString MESSAGE_POST_EVENT_TRAVERSAL;
/*Preframe 该消息表示这一帧已经开始*/
const static dtUtil::RefString MESSAGE_PRE_FRAME;
/*MESSAGE_CAMERA_SYNCH: 这个消息用来在模拟阶段后改变摄像机位置*/
const static dtUtil::RefString MESSAGE_CAMERA_SYNCH;
/*MESSAGE_FRAME_SYNCH: 这个消息允许没帧的行为与摄像头的位置或者相关点相关*/
const static dtUtil::RefString MESSAGE_FRAME_SYNCH;
/*MESSAGE_FRAME:该消息允许dtABC::Application完成OSG更新和渲染*/
const static dtUtil::RefString MESSAGE_FRAME;
/* MESSAGE_POST_FRAME: 这个消息表示该帧结束*/
const static dtUtil::RefString MESSAGE_POST_FRAME;
/* MESSAGE_CONFIG: 这个消息在真循环之前被发出,任何初始化行为应该在这时候被完成 */
const static dtUtil::RefString MESSAGE_CONFIG;
/*MESSAGE_PAUSE: 当system处于暂停状态时,这个消息所引发的行为将代替MESSAGE_PREFRAME 所引发的行为*/
const static dtUtil::RefString MESSAGE_PAUSE;
/*MESSAGE_PAUSE_START: 当SetPause(true)时这个消息被发出,通知应用软件当前暂停已经开始*/
const static dtUtil::RefString MESSAGE_PAUSE_START;
/*MESSAGE_PAUSE_END: 当SetPause(false)时发出该消息.*/
const static dtUtil::RefString MESSAGE_PAUSE_END;
/* MESSAGE_EXIT: 当退出帧循环时发出该消息 */
const static dtUtil::RefString MESSAGE_EXIT;
/*这个消息是用来响应当前帧下的dtABC::Application相应OSG事件。用户不要监听这个事件*/
const static dtUtil::RefString MESSAGE_EVENT_TRAVERSAL;
/*这个消息是在MESSAGE_EVENT_TRAVERSAL后立即发送出去的。可以通过监听这个消息来确定当前帧下前输入或者窗口事件以完成*/
const static dtUtil::RefString MESSAGE_POST_EVENT_TRAVERSAL;
/*Preframe 该消息表示这一帧已经开始*/
const static dtUtil::RefString MESSAGE_PRE_FRAME;
/*MESSAGE_CAMERA_SYNCH: 这个消息用来在模拟阶段后改变摄像机位置*/
const static dtUtil::RefString MESSAGE_CAMERA_SYNCH;
/*MESSAGE_FRAME_SYNCH: 这个消息允许没帧的行为与摄像头的位置或者相关点相关*/
const static dtUtil::RefString MESSAGE_FRAME_SYNCH;
/*MESSAGE_FRAME:该消息允许dtABC::Application完成OSG更新和渲染*/
const static dtUtil::RefString MESSAGE_FRAME;
/* MESSAGE_POST_FRAME: 这个消息表示该帧结束*/
const static dtUtil::RefString MESSAGE_POST_FRAME;
/* MESSAGE_CONFIG: 这个消息在真循环之前被发出,任何初始化行为应该在这时候被完成 */
const static dtUtil::RefString MESSAGE_CONFIG;
/*MESSAGE_PAUSE: 当system处于暂停状态时,这个消息所引发的行为将代替MESSAGE_PREFRAME 所引发的行为*/
const static dtUtil::RefString MESSAGE_PAUSE;
/*MESSAGE_PAUSE_START: 当SetPause(true)时这个消息被发出,通知应用软件当前暂停已经开始*/
const static dtUtil::RefString MESSAGE_PAUSE_START;
/*MESSAGE_PAUSE_END: 当SetPause(false)时发出该消息.*/
const static dtUtil::RefString MESSAGE_PAUSE_END;
/* MESSAGE_EXIT: 当退出帧循环时发出该消息 */
const static dtUtil::RefString MESSAGE_EXIT;
上述消息中关于摄像机的注释可能有些偏差,我会在过后确定后再行修改。
与上述消息对应的函数如下所示
view plaincopy to clipboardprint?
void Config();
void EventTraversal(const double deltaSimTime, const double deltaRealTime);
void PostEventTraversal(const double deltaSimTime, const double deltaRealTime);
void PreFrame(const double deltaSimTime, const double deltaRealTime);
void FrameSynch(const double deltaSimTime, const double deltaRealTime);
void CameraSynch(const double deltaSimTime, const double deltaRealTime);
void Frame(const double deltaSimTime, const double deltaRealTime);
void PostFrame(const double deltaSimTime, const double deltaRealTime);
void Pause(const double deltaRealTime);
void SetPause(bool paused);//根据当前System内部变量mPaused发送暂停开始结束消息
void Config();
void EventTraversal(const double deltaSimTime, const double deltaRealTime);
void PostEventTraversal(const double deltaSimTime, const double deltaRealTime);
void PreFrame(const double deltaSimTime, const double deltaRealTime);
void FrameSynch(const double deltaSimTime, const double deltaRealTime);
void CameraSynch(const double deltaSimTime, const double deltaRealTime);
void Frame(const double deltaSimTime, const double deltaRealTime);
void PostFrame(const double deltaSimTime, const double deltaRealTime);
void Pause(const double deltaRealTime);
void SetPause(bool paused);//根据当前System内部变量mPaused发送暂停开始结束消息
这些事件是如何被发送到BaseABC的我们在前文里面也已经提过了。至于之后引起了什么样的行为,当我们谈到dtABC命名空间时在具体讨论
好了往下我们开始进入帧循环看看这些消息分别在什么情况下被发送出去的。在这里我们先不管关于时间部分 那是下部分要说的 在这我们只关注它的逻辑部分。
view plaincopy to clipboardprint?
void System::Run()
{
mRunning = true;
InitVars();
while(mRunning)
{
StepWindow();
}
LOG_DEBUG("System: Exiting...");
SendMessage(MESSAGE_EXIT);
LOG_DEBUG("System: Done Exiting.");
}
void System::Run()
{
mRunning = true;
InitVars();
while(mRunning)
{
StepWindow();
}
LOG_DEBUG("System: Exiting...");
SendMessage(MESSAGE_EXIT);
LOG_DEBUG("System: Done Exiting.");
}
从上面的函数我们可以看出 在进入帧循环前,我们先要使用InitVars()配置参数,在这个函数里面配置了很多跟时间有关的参数-_-! 然后进入帧循环
view plaincopy to clipboardprint?
void System::StepWindow()
{
SystemStep();
// FIXME how to check if GraphicsWindow is always running ??
// this implementation in really the good way
if(mShutdownOnWindowClose)
{
bool areGraphicsWindow = false;
for(int i = 0; i < DeltaWin::GetInstanceCount() && !areGraphicsWindow; i++)
{
areGraphicsWindow = areGraphicsWindow || DeltaWin::GetInstance(i)->GetOsgViewerGraphicsWindow()->valid();
}
mRunning = mRunning && areGraphicsWindow;
}
}
void System::StepWindow()
{
SystemStep();
// FIXME how to check if GraphicsWindow is always running ??
// this implementation in really the good way
if(mShutdownOnWindowClose)
{
bool areGraphicsWindow = false;
for(int i = 0; i < DeltaWin::GetInstanceCount() && !areGraphicsWindow; i++)
{
areGraphicsWindow = areGraphicsWindow || DeltaWin::GetInstance(i)->GetOsgViewerGraphicsWindow()->valid();
}
mRunning = mRunning && areGraphicsWindow;
}
}
这里在调用SystemStep()函数以后进行了一个判断是否还有窗口,如果窗口都挂掉了也就自动跳出帧循环了。在这里DECLARE_MANAGEMENT_LAYER终于起到作用了,而且还很管用。关于System跟窗口的关系我还没搞明白呢。-_-!以后补上
OK 就先到这了 再往下进行的话就跟那些各种各样的时间参数有关了,还是留在下回说吧-_-!另外System在帧控制方面还有很多函数可以进行灵活的配置,这里就不一一说了。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sxy0082002/archive/2009/06/30/4308340.aspx