OGRE 3D 軟體工程與哲學

My OGRE 3D working journal vol.2

The article will discuss several modules and processes in OGRE.

The way to handle user input event
When the system inits, it calls:

virtual void SampleContext::createInputDevices()
    OIS::Object* obj = mInputMgr->createInputObject(OIS::OISKeyboard, true);
    mKeyboard = static_cast<OIS::Keyboard*>(obj);
    mMouse = static_cast<OIS::Mouse*>(mInputMgr->createInputObject(OIS::OISMouse, true));


where mKeyboard (OIS::Keyboard) and mMouse (OIS::Mouse) belong to SampleContext (see My OGRE working journal vol.1). SampleContext inherits from OIS::KeyListener and OIS::MouseListener, so in th above code snippet, setEventCallback method is used to register SampleBrowser in mKeyboard and mMouse, and therefore we can implement keyPressed, keyReleased, mouseMoved, mousePressed, mouseReleased methods in SampleBrowser. This is a general way for a graphics engine to handle the user input events.
The function call sequence after a user clicks a sample:
→ mTrayMgr->injectMouseUp // mTrayMgr (SdkTrayManager) belongs to SampleBrowser
    → w->_cursorReleased     // links to OgreBites::Button::_cursorReleased
                                              // w is from mWidgets, belonging to SdkTrayManager.
        → mListener->buttonHit  // mListener (SdkTrayListener) 即是 SampleBrowser.
            → SampleBrowser::runSample( ) // loads/starts the sample

In spoken language, what the above does is that clicking the mouse triggers SampleBrowser::mouseReleased, which delegates the task to SampleBrowser:: mTrayMgr. SampleBrowser:: mTrayMgr then calls SampleBrowser::buttonHit, where the implementation can be customized. SampleBrowser:: mTrayMgr manages the GUI components (OgreBites::Widget), and here is the class diagram:

Ogre::Root is a big managing class, and the main rendering loop is in Root::startRendering. mRoot (Ogre::Root) belongs to OgreBites::SampleContext, and there is also mRoot in OgreBites::Sample. It should be noted that Ogre::Root inherits from Ogre::Singleton<Root>, and is a singleton. The mRoot in OgreBites::Sample is actually the same instance as the one in OgreBites::SampleContext.

The way that OGRE implements singleton is to inherit from Singleton<T>, and the client calls getSingleton method to get the singleton instance. Personally I think the way is better than using a big singleton managing class to manage all the system singletons: no need to maintain a big singleton managing class, and individual system singletons maintain their own singleton functionality.

The nice comment about Ogre::Root in OgreRoot.h
/** The root class of the Ogre system.
        The Ogre::Root class represents a starting point for the client
        application. From here, the application can gain access to the
        fundamentals of the system, namely the rendering systems
        available, management of saved configurations, logging, and
        access to other classes in the system. Acts as a hub from which
        all other objects may be reached. An instance of Root must be
        created before any other Ogre operations are called. Once an
        instance has been created, the same instance is accessible
        throughout the life of that object by using Root::getSingleton
        (as a reference) or Root::getSingletonPtr (as a pointer).

The timing of loading the plugins
Take Sample_BSP for example, the executable calls dllStartPlugin in the plugin, creates Sample and SamplePlugin, and registers the SamplePlugin in the Root singleton. The function calls:

–> SampleContext::initApp()
    –> SampleBrowser::setup()
        –> SampleBrowser::loadSamples()
            –> Ogre::Root::loadPlugin()
                  // loads library, gets dllStartPlugin symbol, and calls it.

media file path and loading
media resource is managed by Ogre::ResourceGroupManager, and its instance belongs to Ogre::Root. Ogre::ResourceGroupManager::mResourceGroupManager caches all the media file path information.

In SampleContext::initApp, Ogre::Root is created, and in it Ogre::ResourceGroupManager is created. At this time 3 ResourceGroup (“General”, “Internal”, “Autodetect”) are added to ResourceGroupManager::mResourceGroupManager (map<String, ResourceGroup*>).

In SampleContext::initApp, SampleContext::locateResources is called to load “resources.cfg” or “resources_d.cfg”, create 3 more ResourceGroup (“Essential”, “Popular”, “Tests”), and cache all the media file name in the specified media folder to ResourceGroup::resourceIndexCaseSensitive.

When is the real loading? When the client calls MeshManager::load and/or Entity::setMaterialName, the specified mesh or material related media files will be loaded.

From now on we will quickly go through the 36 samples in OGRE SDK. The point is to see what each sample demonstrates, and how they do it. 
A sample of BSP scene management.
# In Sample_BSP::createSceneManager, BspSceneManager is created.
# Sample_BSP::locateResources and Sample_BSP::loadResources demonstrate how to process the prepared BSP resource.
# BSP scene management needs prepared BSP related resource, which is an issue.
# Currently there are “DefaultSceneManager”, “BspSceneManager”, “PCZSceneManager”, “OctreeSceneManager” in Root::mSceneManagerEnum::mFactories.

Ogre::MeshManager::createBezierPatch can directly generates Bezier surfaces, where the input is control point vertices (in this sample, 9 control points).

A nice graphics sample, including normal mapping, moving light source (and its rendering), and different materials. The point in this sample is actually to realize how OGRE material script works (e.g., “Examples/Athene/NormalMapped” is in “mediamaterialsscriptsExamples-Advanced.material”).

The camera is attached to a scene node, and a spline animation is generated for that node. The effect of the flying view is achieved.
# mCamera (Ogre::Camera) is a kind of Ogre::MovableObject, and can be attached to a scene node.

# Here is the code snippet about how to generate a spline animation track:

// set up a 10 second animation for our camera, using spline interpolation for// nice curves
Animation* anim = mSceneMgr->createAnimation("CameraTrack", 10);

// create a track to animate the camera's node
NodeAnimationTrack* track = anim->createNodeTrack(0, camNode);

// create keyframes for our track
track->createNodeKeyFrame(0)->setTranslate(Vector3(200, 0, 0));
track->createNodeKeyFrame(2.5)->setTranslate(Vector3(0, -50, 100));
track->createNodeKeyFrame(5)->setTranslate(Vector3(-500, 100, 0));
track->createNodeKeyFrame(7.5)->setTranslate(Vector3(0, 200, -300));
track->createNodeKeyFrame(10)->setTranslate(Vector3(200, 0, 0));
# Update the current time in animation, and the animation will be played (call Ogre::AnimationState::addTime).

Uses the material “Examples/CelShading” in the file “Examples-Advanced.material”. And we can also see how to pass the parameters to shader for each SubEntity in this sample.