Difference between revisions of "Io scene geo"

From The Foundry MODO SDK wiki
Jump to: navigation, search
 
(4 intermediate revisions by the same user not shown)
Line 11: Line 11:
 
===Class Declarations===
 
===Class Declarations===
  
Here we want to create a default line parser, so we have CGEOParser inherit from CLxLineParser and don't redeclare any functions. We do, however, declare two additional functions that will strip white space from the start and end of lines and skip blank lines.
+
Here we want to create a custom line parser, so we have CGEOParser inherit from CLxLineParser.  
  
<pre>
+
The CLxLineParser class is slightly different in terms of subclassing it as it is itself already a fully functional parser for general line-based text formats. As such, we don't have to rewrite any functions to get it to work. Instead, subclassing it allows us to alter some of the settings of the original parser to get it to handle how we want. We do this by redeclaring the function that corresponds to the setting we want and having it return true instead of false. In our case, we redeclare functions which cause the parser to strip white space from the start and end of lines and skip blank lines.
 +
 
 +
This class is later called to make another class into a file parser with the setting we have specified.
 +
 
 +
<syntaxhighlight>
 
class CGEOParser : public CLxLineParser
 
class CGEOParser : public CLxLineParser
 
{
 
{
Line 20: Line 24:
 
         virtual bool lp_SkipBlank  () { return true; }
 
         virtual bool lp_SkipBlank  () { return true; }
 
};
 
};
</pre>
+
</syntaxhighlight>
 +
 
 +
This class is really two classes in one. We want our class to load scenes, but before we load the scenes we need to parse them, so we need to subclass so this class can do both.
 +
 
 +
First off, we want this class to be its own parser, so we inherit from the custom line parser we just created, CGEOParser. We give access to this parser through the sl_Parser method class, which returns the parser through a this pointer.
  
Our loader, titled CGEOLoader, is a subclass of the CLxSceneLoader utility class, where we have overloaded the recognition and parsing methods.  We also also want it to be its own parser, so we inherit from CGEOParser, and then return the parser through the sl_Parser() method.
+
Secondly, we want our class to be a scene loader, so we inherit from CLxSceneLoader. All of the utilities for loading the scene we get from CLxSceneLoader. However, we also want this class to be a parser, so we need to override the recognize method, which verifies that the file we are going to parse is valid, and the parsing methods.
  
<pre>class CGEOLoader : public CLxSceneLoader, public CGEOParser
+
<syntaxhighlight>
 +
class CGEOLoader : public CLxSceneLoader, public CGEOParser
 
{
 
{
 
     public:
 
     public:
Line 39: Line 48:
 
         static LXtTagInfoDesc descInfo[];
 
         static LXtTagInfoDesc descInfo[];
 
};
 
};
</pre>
+
</syntaxhighlight>
  
We create CGEOFormat, which defines a line format. As a result we inherit from CLxLineFormat. The format we create is just a default line format with spaces delimiting elements on the same line.
+
We want this class to set the format for the GEO file we will be saving. As such, we inherit from CLxLineFormat, which defines all the methods for a vanilla format for line-based text files. The only function we redeclare is lf_Separator, which governs what separates each element.  By setting the function to return a space we make it so that there is a space separating elements on the same line.
  
 +
<syntaxhighlight>
 
  class CGEOFormat : public CLxLineFormat
 
  class CGEOFormat : public CLxLineFormat
 
  {
 
  {
Line 48: Line 58:
 
         virtual const char * lf_Separator  () { return " "; }
 
         virtual const char * lf_Separator  () { return " "; }
 
  };
 
  };
 +
</syntaxhighlight>
  
We want to create a utility that saves scenes, so we inherit from CLxSceneSaver. We also want the saver to have it's own format, so we inherit the format we created above.
+
Like the loader, we want this class to be two things: a saver and a format and its own format. We want this class to be its own format so that when we call the methods to save the file we have the format in which it is going to be saved already defined. We give access to the format through the ss_Format function, which returns the format through a this pointer.
  
 +
To also give the class the ability to save scenes we inherit from CLxSceneSaver. We have a custom format for our file, so we have redeclared all the functions that deal with the file(Verify, Save, Point, Polygon). 
 +
 +
The GatherColors() function stores the colors of the polygons in the scene.
 +
 +
The Verify method is an optional method that displays to the user what will happen to their data with the format that we are saving it in. The Save function is run after the Verify function, and calls the GatherColors function as well as the WritePolys function; the WritePolys function, in turn, calls Point and Polygon.
 +
 +
<syntaxhighlight>
 
  class CGEOSaver : public CLxSceneSaver, public CGEOFormat
 
  class CGEOSaver : public CLxSceneSaver, public CGEOFormat
 
  {
 
  {
Line 57: Line 75:
 
   
 
   
 
         virtual void ss_Verify    ();
 
         virtual void ss_Verify    ();
         virtual LxResult ss_Save      ();
+
         virtual LxResult         ss_Save      ();
 
         virtual void ss_Point    ();
 
         virtual void ss_Point    ();
 
         virtual void ss_Polygon  ();
 
         virtual void ss_Polygon  ();
Line 70: Line 88:
 
         bool get_matr;
 
         bool get_matr;
 
  };
 
  };
 +
</syntaxhighlight>
  
 
===[[Initialize_(index)|Initialize]]===
 
===[[Initialize_(index)|Initialize]]===
 +
 +
Intialize is called when we add the plugin to modo, and is the utility that exports the server. The LXx_ADD_SERVER method is simply a wrapper that is identical to normal method of adding a server, with the arguments being (interface_to_be_added, class_you_depend_on, server_name).
  
 
This section exports the [[Overview#nexus_Servers|servers]] we create. In this case, we export a [[Saver_Object|saver]] and a [[Loader_Object|loader]] that take the contents of CGEOLoader and CGEOSaver respectively. They are both given the name vs_GEO.
 
This section exports the [[Overview#nexus_Servers|servers]] we create. In this case, we export a [[Saver_Object|saver]] and a [[Loader_Object|loader]] that take the contents of CGEOLoader and CGEOSaver respectively. They are both given the name vs_GEO.
  
 +
<syntaxhighlight>
 
         void
 
         void
 
  initialize ()
 
  initialize ()
Line 81: Line 103:
 
         LXx_ADD_SERVER (Saver,  CGEOSaver,  "vs_GEO");
 
         LXx_ADD_SERVER (Saver,  CGEOSaver,  "vs_GEO");
 
  }
 
  }
 +
</syntaxhighlight>
  
 
===[[Server_Tags|Server Tags]]===
 
===[[Server_Tags|Server Tags]]===
 +
Servers tags are examined when the server is initialized, and give information about the server. We set the tags in this case by taking descinfo[] arrays and associating the relevant data with the corresponding flags.
  
Server tags define the format of the loader, its name and file pattern. The tags here indicate that the plugin is a scene loader that loads a mesh.
+
The tags here indicate that the plugin is a scene loader that loads a mesh.
  
 +
<syntaxhighlight>
 
  LXtTagInfoDesc CGEOLoader::descInfo[] = {
 
  LXtTagInfoDesc CGEOLoader::descInfo[] = {
 
         { LXsLOD_CLASSLIST, LXa_SCENE },
 
         { LXsLOD_CLASSLIST, LXa_SCENE },
Line 92: Line 117:
 
         { 0 }
 
         { 0 }
 
  };
 
  };
 +
</syntaxhighlight>
  
 
Similarly, since the saver in this plugin loads a mesh it is a scene saver
 
Similarly, since the saver in this plugin loads a mesh it is a scene saver
  
 +
<syntaxhighlight>
 
  LXtTagInfoDesc CGEOSaver::descInfo[] = {
 
  LXtTagInfoDesc CGEOSaver::descInfo[] = {
 
         { LXsSAV_OUTCLASS, LXa_SCENE },
 
         { LXsSAV_OUTCLASS, LXa_SCENE },
Line 101: Line 128:
 
         { 0 }
 
         { 0 }
 
  };
 
  };
 +
</syntaxhighlight>
  
 
===Implementation===
 
===Implementation===
  
Recognition method reads the first line and sees if it matches the sync pattern.  Returns true for a match.
+
The Recognize method reads the first line and sees if it matches the sync pattern.  It returns true for a match, meaning that the file is in the format that we want.
  
 +
<syntaxhighlight>
 
         bool
 
         bool
 
  CGEOLoader::sl_Recognize ()
 
  CGEOLoader::sl_Recognize ()
 
  {
 
  {
         return lp_ReadLine () && (strcmp ("3DG1", line_buf) == 0);
+
         ...
 
  }
 
  }
 +
</syntaxhighlight>
  
 
Parsing the recognized file (which has been reset to the start again) consists of three phases.  The init method adds a mesh item to the scene and reads past the sync line.
 
Parsing the recognized file (which has been reset to the start again) consists of three phases.  The init method adds a mesh item to the scene and reads past the sync line.
  
 +
<syntaxhighlight>
 
         bool
 
         bool
 
  CGEOLoader::sl_ParseInit ()
 
  CGEOLoader::sl_ParseInit ()
 
  {
 
  {
        color_list.clear ();
+
      ...
        read_vrts = true;
+
+
        scene_build.AddMesh ();
+
        return lp_ReadLine ();
+
 
  }
 
  }
 +
</syntaxhighlight>
  
 
The sl_Parse() method is called as long as it returns true.
 
The sl_Parse() method is called as long as it returns true.
  
<pre>
+
<syntaxhighlight>
 
         bool
 
         bool
 
CGEOLoader::sl_Parse (
 
CGEOLoader::sl_Parse (
 
         LxResult *error)
 
         LxResult *error)
 
{
 
{
         unsigned n, i, k;
+
         ...
        double x, y, z;
+
        string col;
+
 
+
        /*
+
        * One EOF this will return false which will terminate parsing.
+
        */
+
        if (!lp_ReadLine ())
+
                return false;
+
 
+
        /*
+
        * The first thing we read is the number of points. We'll just loop
+
        * over that many lines here and create points for them all. This sets
+
        * the flag false so that this only happens once.
+
        */
+
        if (read_vrts) {
+
                if (!PullNum (&n))
+
                        return false;
+
 
+
                for (i = 0; i < n; i++) {
+
                        if (!lp_ReadLine ())
+
                                return false;
+
 
+
                        if (!PullNum (&x) || !PullNum (&y) || !PullNum (&z))
+
                                return false;
+
 
+
                        scene_build.AddPoint (x, y, z);
+
                }
+
 
+
                read_vrts = false;
+
                return true;
+
        }
+
 
+
        /*
+
        * On subsequent calls to sl_Parse() we process one polygon at a time.
+
        * We first look for the number of vertices.  Those are read one at a
+
        * time and a polygon is created.
+
        */
+
        if (!PullNum (&n))
+
                return false;
+
 
+
        scene_build.StartPoly (LXiPTYP_FACE);
+
 
+
        for (i = 0; i < n; i++) {
+
                if (!PullNum (&k))
+
                        return false;
+
 
+
                scene_build.AddVertex (k);
+
        }
+
 
+
        k = scene_build.AddPolygon ();
+
 
+
        /*
+
        * The final value is the color name, which we store in a list and set
+
        * as the material tag.
+
        */
+
        PullWhite ();
+
        PullNonWhite (col);
+
        if (col.length ()) {
+
                color_list.push_back (col);
+
                scene_build.SetPolyTag (k, LXi_PTAG_MATR, col.c_str ());
+
        }
+
        return true;
+
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
The done method is called when parsing is complete.  We collapse the list of colors that we created during parsing and create materials for them all.
 
The done method is called when parsing is complete.  We collapse the list of colors that we created during parsing and create materials for them all.
  
 +
<syntaxhighlight>
 
         bool
 
         bool
 
  CGEOLoader::sl_ParseDone ()
 
  CGEOLoader::sl_ParseDone ()
 
  {
 
  {
        list<string>::iterator itr;
+
      ...
        double rgb[3];
+
        int hex, i;
+
+
        color_list.sort ();
+
        color_list.unique ();
+
+
        for (itr = color_list.begin (); itr != color_list.end (); itr++) {
+
                hex = 0x808080;
+
                sscanf (itr->c_str (), "%x", &hex);
+
                for (i = 0; i < 3; i++) {
+
                        rgb[2 - i] = (hex & 0xFF) / 255.0;
+
                        hex = hex >> 8;
+
                }
+
+
                scene_build.AddMaterial (itr->c_str ());
+
                scene_build.SetChannel (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".R", rgb[0]);
+
                scene_build.SetChannel (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".G", rgb[1]);
+
                scene_build.SetChannel (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".B", rgb[2]);
+
        }
+
        return true;
+
 
  }
 
  }
 +
</syntaxhighlight>
  
 
The optional ss_Verify() method can be used to display a message to the user about what will happen to their data using this format.  Ideally a real message table should be used to support translation, but common message 2020 allows for a general string. The scene could be examined at this point to determine if there was anything that would be lost.
 
The optional ss_Verify() method can be used to display a message to the user about what will happen to their data using this format.  Ideally a real message table should be used to support translation, but common message 2020 allows for a general string. The scene could be examined at this point to determine if there was anything that would be lost.
  
 +
<syntaxhighlight>
 
         void
 
         void
 
  CGEOSaver::ss_Verify ()
 
  CGEOSaver::ss_Verify ()
 
  {
 
  {
         Message ("common", 2020);
+
         ...
        MessageArg (1, "GEO supports geometry from a single layer, and no texturing.");
+
 
  }
 
  }
 +
</syntaxhighlight>
  
 
The save method performs the actual save. Note that this is called twice; first in a dummy mode with the format output disabled and then for real. In the dummy mode the WritePoints() and WritePolys() calls will not do anything except tabulate how many elements would be traversed in the real case. Because of the multiple passes and the fact that this same instance of the saver is used for all saving during a single session, all persistent states should be reset between uses.
 
The save method performs the actual save. Note that this is called twice; first in a dummy mode with the format output disabled and then for real. In the dummy mode the WritePoints() and WritePolys() calls will not do anything except tabulate how many elements would be traversed in the real case. Because of the multiple passes and the fact that this same instance of the saver is used for all saving during a single session, all persistent states should be reset between uses.
  
<pre>
+
<syntaxhighlight>
 
         LxResult
 
         LxResult
 
CGEOSaver::ss_Save ()
 
CGEOSaver::ss_Save ()
 
{
 
{
        bool mesh;
+
      ...
        unsigned npts;
+
 
+
        /*
+
        * Count points in all meshes.
+
        */
+
        npts = 0;
+
        StartScan ();
+
        while (mesh = NextMesh ())
+
                npts += PointCount ();
+
 
+
        /*
+
        * Tabulate the colors of all the materials.
+
        */
+
        get_matr = true;
+
        StartScan ();
+
        while (mesh = NextMesh ())
+
                WritePolys ();
+
 
+
        GatherColors ();
+
 
+
        /*
+
        * Write the sync line and the number of points.
+
        */
+
        lf_Output ("3DG1");
+
        lf_Break ();
+
        lf_Output (npts);
+
        lf_Break ();
+
 
+
        /*
+
        * Write point positions.
+
        */
+
        pnt_count = 0;
+
        StartScan ();
+
        while (mesh = NextMesh ())
+
                WritePoints ();
+
 
+
        /*
+
        * Write polygons.
+
        */
+
        get_matr = false;
+
        StartScan ();
+
        while (mesh = NextMesh ())
+
                WritePolys ();
+
 
+
        /*
+
        * Clear any persistent state.
+
        */
+
        matr_color.clear ();
+
        pnt_index.clear ();
+
        return LXe_OK;
+
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
This is called for every polygon in the current mesh from WritePolys(). We make two passes through the polygons -- the first to collect the mask tags, and the second to actually write the polygons.  Writing just writes the number of vertices, the vertex indicies, and the color for the polygon's material.
 
This is called for every polygon in the current mesh from WritePolys(). We make two passes through the polygons -- the first to collect the mask tags, and the second to actually write the polygons.  Writing just writes the number of vertices, the vertex indicies, and the color for the polygon's material.
  
 +
<syntaxhighlight>
 
         void
 
         void
 
  CGEOSaver::ss_Polygon ()
 
  CGEOSaver::ss_Polygon ()
 
  {
 
  {
        if (get_matr) {
+
      ...
                const char *mask = PolyTag (LXi_PTAG_MATR);
+
                if (mask)
+
                        matr_color[mask] = 0;
+
                return;
+
        }
+
+
        unsigned i, n;
+
        char buf[32];
+
+
        n = PolyNumVerts ();
+
        lf_Output (n);
+
+
        for (i = 0; i < n; i++)
+
                lf_Output (pnt_index[PolyVertex (i)]);
+
+
        sprintf (buf, "0x%06X", matr_color[PolyTag (LXi_PTAG_MATR)]);
+
        lf_Output (buf);
+
        lf_Break ();
+
 
  }
 
  }
 +
</syntaxhighlight>
  
 
After the first pass through the polygons, we compute the color for each material tag that we stored in the map. This simply looks up the mask for the tag and looks through its layers.  The first material that it finds we read the diffuse color. This can't be done while enumerating polygons because only one item scan can be performed at once and we're already scanning meshes.
 
After the first pass through the polygons, we compute the color for each material tag that we stored in the map. This simply looks up the mask for the tag and looks through its layers.  The first material that it finds we read the diffuse color. This can't be done while enumerating polygons because only one item scan can be performed at once and we're already scanning meshes.
  
<pre>
+
<syntaxhighlight>
 
         void
 
         void
 
CGEOSaver::GatherColors ()
 
CGEOSaver::GatherColors ()
 
{
 
{
         map<string,unsigned>::iterator it;
+
         ...
        const char *mask;
+
        double rgb[3];
+
        unsigned col, cmp, i;
+
 
+
        for (it = matr_color.begin (); it != matr_color.end (); it++) {
+
                mask = it->first.c_str ();
+
                if (!ScanMask (mask)) {
+
                        matr_color[mask] = 0x808080;
+
                        continue;
+
                }
+
 
+
                while (NextLayer ()) {
+
                        if (strcmp (ItemType (), LXsITYPE_ADVANCEDMATERIAL))
+
                                continue;
+
 
+
                        rgb[0] = ChanFloat (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".R");
+
                        rgb[1] = ChanFloat (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".G");
+
                        rgb[2] = ChanFloat (LXsICHAN_ADVANCEDMATERIAL_DIFFCOL".B");
+
 
+
                        col = 0;
+
                        for (i = 0; i < 3; i++) {
+
                                if (rgb[i] <= 0.0)
+
                                        cmp = 0;
+
                                else if (rgb[i] >= 1.0)
+
                                        cmp = 255;
+
                                else
+
                                        cmp = (int) (rgb[i] * 255 + 0.5);
+
 
+
                                col = (col << 8) + cmp;
+
                        }
+
 
+
                        matr_color[mask] = col;
+
                }
+
        }
+
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
This method is called for every point during WritePoints(). We write the coordinates to a line and save the index of the point in our map.
 
This method is called for every point during WritePoints(). We write the coordinates to a line and save the index of the point in our map.
  
 +
<syntaxhighlight>
 
         void
 
         void
 
  CGEOSaver::ss_Point ()
 
  CGEOSaver::ss_Point ()
 
  {
 
  {
         float vec[3];
+
         ...
+
        PntPosition (vec);
+
        lf_Output (vec[0]);
+
        lf_Output (vec[1]);
+
        lf_Output (vec[2]);
+
        lf_Break ();
+
+
        pnt_index[PntID ()] = pnt_count++;
+
 
  }
 
  }
 +
</syntaxhighlight>
 +
 +
[[Category: SDK Examples]]

Latest revision as of 19:09, 16 September 2013

Io_scene_geo is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.

When installed, this plugin adds a saver/loader that loads/saves VideoScape Geo files.

Geo menu.png

Geo saver option highlighted above.

Code Walkthrough

Class Declarations

Here we want to create a custom line parser, so we have CGEOParser inherit from CLxLineParser.

The CLxLineParser class is slightly different in terms of subclassing it as it is itself already a fully functional parser for general line-based text formats. As such, we don't have to rewrite any functions to get it to work. Instead, subclassing it allows us to alter some of the settings of the original parser to get it to handle how we want. We do this by redeclaring the function that corresponds to the setting we want and having it return true instead of false. In our case, we redeclare functions which cause the parser to strip white space from the start and end of lines and skip blank lines.

This class is later called to make another class into a file parser with the setting we have specified.

class CGEOParser : public CLxLineParser
{
    public:
        virtual bool		 lp_StripWhite ()	{ return true; }
        virtual bool		 lp_SkipBlank  ()	{ return true; }
};

This class is really two classes in one. We want our class to load scenes, but before we load the scenes we need to parse them, so we need to subclass so this class can do both.

First off, we want this class to be its own parser, so we inherit from the custom line parser we just created, CGEOParser. We give access to this parser through the sl_Parser method class, which returns the parser through a this pointer.

Secondly, we want our class to be a scene loader, so we inherit from CLxSceneLoader. All of the utilities for loading the scene we get from CLxSceneLoader. However, we also want this class to be a parser, so we need to override the recognize method, which verifies that the file we are going to parse is valid, and the parsing methods.

class CGEOLoader : public CLxSceneLoader, public CGEOParser
{
    public:
        virtual CLxFileParser *	 sl_Parser    ()	{ return this; }
 
        virtual bool		 sl_Recognize ();
        virtual bool		 sl_ParseInit ();
        virtual bool		 sl_ParseDone ();
        virtual bool		 sl_Parse     (LxResult *);
 
        bool			 read_vrts;
        list<string>		 color_list;
 
        static LXtTagInfoDesc	 descInfo[];
};

We want this class to set the format for the GEO file we will be saving. As such, we inherit from CLxLineFormat, which defines all the methods for a vanilla format for line-based text files. The only function we redeclare is lf_Separator, which governs what separates each element. By setting the function to return a space we make it so that there is a space separating elements on the same line.

 class CGEOFormat : public CLxLineFormat
 {
    public:
        virtual const char *	 lf_Separator  ()		{ return " "; }
 };

Like the loader, we want this class to be two things: a saver and a format and its own format. We want this class to be its own format so that when we call the methods to save the file we have the format in which it is going to be saved already defined. We give access to the format through the ss_Format function, which returns the format through a this pointer.

To also give the class the ability to save scenes we inherit from CLxSceneSaver. We have a custom format for our file, so we have redeclared all the functions that deal with the file(Verify, Save, Point, Polygon).

The GatherColors() function stores the colors of the polygons in the scene.

The Verify method is an optional method that displays to the user what will happen to their data with the format that we are saving it in. The Save function is run after the Verify function, and calls the GatherColors function as well as the WritePolys function; the WritePolys function, in turn, calls Point and Polygon.

 class CGEOSaver : public CLxSceneSaver, public CGEOFormat
 {
    public:
        virtual CLxFileFormat *	 ss_Format    ()	{ return this; }
 
        virtual void		 ss_Verify    ();
        virtual LxResult	         ss_Save      ();
        virtual void		 ss_Point     ();
        virtual void		 ss_Polygon   ();
 
        void			 GatherColors ();
 
        static LXtTagInfoDesc	 descInfo[];
 
        map<LXtPointID,unsigned> pnt_index;
        unsigned		 pnt_count;
        map<string,unsigned>	 matr_color;
        bool			 get_matr;
 };

Initialize

Intialize is called when we add the plugin to modo, and is the utility that exports the server. The LXx_ADD_SERVER method is simply a wrapper that is identical to normal method of adding a server, with the arguments being (interface_to_be_added, class_you_depend_on, server_name).

This section exports the servers we create. In this case, we export a saver and a loader that take the contents of CGEOLoader and CGEOSaver respectively. They are both given the name vs_GEO.

        void
 initialize ()
 {
        LXx_ADD_SERVER (Loader, CGEOLoader, "vs_GEO");
        LXx_ADD_SERVER (Saver,  CGEOSaver,  "vs_GEO");
 }

Server Tags

Servers tags are examined when the server is initialized, and give information about the server. We set the tags in this case by taking descinfo[] arrays and associating the relevant data with the corresponding flags.

The tags here indicate that the plugin is a scene loader that loads a mesh.

 LXtTagInfoDesc	 CGEOLoader::descInfo[] = {
        { LXsLOD_CLASSLIST,	LXa_SCENE	},
        { LXsLOD_DOSPATTERN,	"*.geo"		},
        { LXsSRV_USERNAME,	"VideoScape GEO"},
        { 0 }
 };

Similarly, since the saver in this plugin loads a mesh it is a scene saver

 LXtTagInfoDesc	 CGEOSaver::descInfo[] = {
        { LXsSAV_OUTCLASS,	LXa_SCENE	},
        { LXsSAV_DOSTYPE,	"GEO"		},
        { LXsSRV_USERNAME,	"VideoScape GEO"},
        { 0 }
 };

Implementation

The Recognize method reads the first line and sees if it matches the sync pattern. It returns true for a match, meaning that the file is in the format that we want.

        bool
 CGEOLoader::sl_Recognize ()
 {
        ...
 }

Parsing the recognized file (which has been reset to the start again) consists of three phases. The init method adds a mesh item to the scene and reads past the sync line.

        bool
 CGEOLoader::sl_ParseInit ()
 {
       ...
 }

The sl_Parse() method is called as long as it returns true.

        bool
CGEOLoader::sl_Parse (
        LxResult		*error)
{
        ...
}

The done method is called when parsing is complete. We collapse the list of colors that we created during parsing and create materials for them all.

        bool
 CGEOLoader::sl_ParseDone ()
 {
       ...
 }

The optional ss_Verify() method can be used to display a message to the user about what will happen to their data using this format. Ideally a real message table should be used to support translation, but common message 2020 allows for a general string. The scene could be examined at this point to determine if there was anything that would be lost.

        void
 CGEOSaver::ss_Verify ()
 {
        ...
 }

The save method performs the actual save. Note that this is called twice; first in a dummy mode with the format output disabled and then for real. In the dummy mode the WritePoints() and WritePolys() calls will not do anything except tabulate how many elements would be traversed in the real case. Because of the multiple passes and the fact that this same instance of the saver is used for all saving during a single session, all persistent states should be reset between uses.

        LxResult
CGEOSaver::ss_Save ()
{
       ...
}

This is called for every polygon in the current mesh from WritePolys(). We make two passes through the polygons -- the first to collect the mask tags, and the second to actually write the polygons. Writing just writes the number of vertices, the vertex indicies, and the color for the polygon's material.

        void
 CGEOSaver::ss_Polygon ()
 {
       ...
 }

After the first pass through the polygons, we compute the color for each material tag that we stored in the map. This simply looks up the mask for the tag and looks through its layers. The first material that it finds we read the diffuse color. This can't be done while enumerating polygons because only one item scan can be performed at once and we're already scanning meshes.

        void
CGEOSaver::GatherColors ()
{
        ...
}

This method is called for every point during WritePoints(). We write the coordinates to a line and save the index of the point in our map.

        void
 CGEOSaver::ss_Point ()
 {
        ...
 }