Progress updates while loading/saving an Irrlicht scene file

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Progress updates while loading/saving an Irrlicht scene file

Post by vitek »

There was a discussion about displaying progress updates while loading files. This is an easy way to show progress when loading or saving scene files, and it requires no changes to the Irrlicht source.

Code: Select all

class CProgressSceneUserDataReader : public ISceneUserDataSerializer
{
   IFileSystem* FileSystem;

   void (*RdCallbackFn)(void*, s32, s32);
   void* RdCallbackP;

   s32 TotalSceneNodes;
   s32 ReadSceneNodes;

public:
   CProgressSceneUserDataReader(IFileSystem* fileSystem)
      : FileSystem(fileSystem)
      , RdCallbackFn(0)
      , RdCallbackP(0)
      , TotalSceneNodes(0)
      , ReadSceneNodes(0)
   {
      FileSystem->grab();
   }

   virtual ~CProgressSceneUserDataReader()
   {
      FileSystem->drop();
   }

   void setProgressCb(void (*cb)(void*, s32, s32), void* param)
   {
      RdCallbackFn = cb;
      RdCallbackP  = param;
   }

   //! Called when the scene manager read a scene node while loading a file.
   virtual void OnReadUserData(ISceneNode* forSceneNode, io::IAttributes* userData)
   {
      // if we've never read total scene nodes, then we assume this is the top level
      // scene node. there is no nice way to identify the root scene node.
      if (!TotalSceneNodes)
      {
         TotalSceneNodes = userData->getAttributeAsInt("NodeCount");
         ReadSceneNodes  = 0;
      }

      ++ReadSceneNodes;
      if (RdCallbackFn && (TotalSceneNodes != 0))
         (*RdCallbackFn)(RdCallbackP, ReadSceneNodes, TotalSceneNodes);
   }

   //! Called when the scene manager is writing a scene node to an xml file for example.
   virtual io::IAttributes* createUserData(ISceneNode* forSceneNode)
   {
      // no-op. this class is for reading only.
      return 0;
   }
};

class CProgressSceneUserDataWriter : public ISceneUserDataSerializer
{
   IFileSystem* FileSystem;

   void (*WrCallbackFn)(void*, s32, s32);
   void* WrCallbackP;

   s32 TotalSceneNodes;
   s32 WrittenSceneNodes;

public:
   CProgressSceneUserDataWriter(IFileSystem* fileSystem)
      : FileSystem(fileSystem)
      , WrCallbackFn(0)
      , WrCallbackP(0)
      , TotalSceneNodes(0)
      , WrittenSceneNodes(0)
   {
      FileSystem->grab();
   }

   virtual ~CProgressSceneUserDataWriter()
   {
      FileSystem->drop();
   }

   void setProgressCb(void (*cb)(void*, s32, s32), void* param)
   {
      WrCallbackFn = cb;
      WrCallbackP  = param;
   }

   //! Called when the scene manager read a scene node while loading a file.
   virtual void OnReadUserData(ISceneNode* forSceneNode, IAttributes* userData)
   {
      // no-op. this class is for writing only
   }

   //! Called when the scene manager is writing a scene node to an xml file for example.
   virtual IAttributes* createUserData(ISceneNode* forSceneNode)
   {
      IAttributes* newAttributes = 0;

      // if TotalSceneNodes is 0 then this is the first call
      if (!TotalSceneNodes)
      {
         // get count of all scene nodes including this one
         getNodeCount(forSceneNode, TotalSceneNodes);
         WrittenSceneNodes = 0;

         newAttributes = FileSystem->createEmptyAttributes();
         if (!newAttributes)
            return 0;

         // add the node count to the attributes
         newAttributes->addInt("NodeCount", TotalSceneNodes);
      }
      else
      {
         // this is necessary so that our read callback gets notified
         // of a new node.
         newAttributes = FileSystem->createEmptyAttributes();
      }

      ++WrittenSceneNodes;
      if (WrCallbackFn && (TotalSceneNodes != 0))
         (*WrCallbackFn)(WrCallbackP, WrittenSceneNodes, TotalSceneNodes);

      return newAttributes;
   }

protected:
   void getNodeCount(ISceneNode* node, s32& nodeCount)
   {
      core::list<ISceneNode*>::Iterator head = node->getChildren().begin();
      core::list<ISceneNode*>::Iterator tail = node->getChildren().end();

      for (; head != tail; ++head)
         getNodeCount(*head, nodeCount);

      nodeCount += 1;
   }
};
To use it...

Code: Select all

void testProgressCb(void*, s32 n, s32 t)
{
   printf("%0.2f%%\n", 100.f * n / t);
}

int main(int argc, char* argv[])
{
   //...

   // to write out a scene
   CProgressSceneUserDataWriter writer(device->getFileSystem());
   writer.setProgressCb(testProgressCb, 0);
   smgr->saveScene("../../media/example2.irr", &writer);

   // to read back a scene
   CProgressSceneUserDataReader reader(device->getFileSystem());
   reader.setProgressCb(testProgressCb, 0);
   smgr->loadScene("../../media/example2.irr", &reader);

   //...
}
Of course it requires that the .irr file be written out using the provided writer, but it is easy enough to convert the .irr file by loading it and then writing it out again before deploying your project.

Here is the output of loading the converted example.irr.

Code: Select all

Irrlicht Engine version 1.1
Microsoft Windows XP Professional Service Pack 2 (Build 2600)
Using renderer: Direct3D 9.0
NVIDIA GeForce 6200 TurboCache(TM) nv4_disp.dll 6.14.10.8421
Loaded texture: #DefaultFont
12.50%
Loaded mesh: ../../media/room.3ds
Loaded texture: ../../media/wall.jpg
25.00%
37.50%
Loaded texture: ../../media/particlewhite.bmp
50.00%
62.50%
Loaded texture: ../../media/rockwall.bmp
75.00%
87.50%
Loaded texture: ../../media/particlered.bmp
100.00%
Finished loading scene: ../../media/example2.irr
Travis
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

There are a few downsides to doing this. One is that it makes the .irr files larger by adding an extra empty entry to every scene node. The other is that if you are already using the serializer for something else, you have to write additional code to support additional serializers.

Of course it would be trivial to apply something like this to the scene manager so that it could report back on progress after loading each node in the scene.
Post Reply