Difference between revisions of "Io scene geo"
Line 10: | Line 10: | ||
===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. | ||
<pre> | <pre> | ||
Line 19: | Line 21: | ||
}; | }; | ||
</pre> | </pre> | ||
− | + | ||
+ | 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. | ||
<pre>class CGEOLoader : public CLxSceneLoader, public CGEOParser | <pre>class CGEOLoader : public CLxSceneLoader, public CGEOParser | ||
Line 38: | Line 41: | ||
</pre> | </pre> | ||
− | + | 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. | |
class CGEOFormat : public CLxLineFormat | class CGEOFormat : public CLxLineFormat | ||
Line 46: | Line 49: | ||
}; | }; | ||
− | We create | + | 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. |
class CGEOSaver : public CLxSceneSaver, public CGEOFormat | class CGEOSaver : public CLxSceneSaver, public CGEOFormat | ||
Line 67: | Line 70: | ||
bool get_matr; | bool get_matr; | ||
}; | }; | ||
− | |||
− | |||
===[[Initialize_(index)|Initialize]]=== | ===[[Initialize_(index)|Initialize]]=== | ||
+ | |||
+ | 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. | ||
void | void | ||
Line 78: | Line 81: | ||
LXx_ADD_SERVER (Saver, CGEOSaver, "vs_GEO"); | LXx_ADD_SERVER (Saver, CGEOSaver, "vs_GEO"); | ||
} | } | ||
− | |||
− | |||
===[[Server_Tags|Server Tags]]=== | ===[[Server_Tags|Server Tags]]=== | ||
+ | |||
+ | 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. | ||
LXtTagInfoDesc CGEOLoader::descInfo[] = { | LXtTagInfoDesc CGEOLoader::descInfo[] = { | ||
Line 90: | Line 93: | ||
}; | }; | ||
− | + | Similarly, since the saver in this plugin loads a mesh it is a scene saver | |
LXtTagInfoDesc CGEOSaver::descInfo[] = { | LXtTagInfoDesc CGEOSaver::descInfo[] = { | ||
Line 98: | Line 101: | ||
{ 0 } | { 0 } | ||
}; | }; | ||
− | |||
− | |||
===Implementation=== | ===Implementation=== | ||
+ | |||
+ | Recognition method reads the first line and sees if it matches the sync pattern. Returns true for a match. | ||
bool | bool | ||
Line 109: | Line 112: | ||
} | } | ||
− | + | 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 | bool | ||
Line 121: | Line 124: | ||
} | } | ||
− | + | The sl_Parse() method is called as long as it returns true. | |
<pre> | <pre> | ||
Line 194: | Line 197: | ||
</pre> | </pre> | ||
− | The | + | 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 | bool | ||
Line 222: | Line 225: | ||
} | } | ||
− | The | + | 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 | void | ||
Line 231: | Line 234: | ||
} | } | ||
− | + | 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 | + | |
<pre> | <pre> | ||
Line 292: | Line 294: | ||
</pre> | </pre> | ||
− | + | 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 | void | ||
Line 318: | Line 320: | ||
} | } | ||
− | + | 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> | <pre> | ||
Line 362: | Line 364: | ||
</pre> | </pre> | ||
− | + | 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 | void | ||
Line 378: | Line 379: | ||
pnt_index[PntID ()] = pnt_count++; | pnt_index[PntID ()] = pnt_count++; | ||
} | } | ||
− | |||
− |
Revision as of 21:38, 10 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 saver option highlighted above.
Contents
Code Walkthrough
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.
class CGEOParser : public CLxLineParser { public: virtual bool lp_StripWhite () { return true; } virtual bool lp_SkipBlank () { return true; } };
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.
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 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.
class CGEOFormat : public CLxLineFormat { public: virtual const char * lf_Separator () { return " "; } };
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.
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
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
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.
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
Recognition method reads the first line and sees if it matches the sync pattern. Returns true for a match.
bool CGEOLoader::sl_Recognize () { return lp_ReadLine () && (strcmp ("3DG1", line_buf) == 0); }
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 () { color_list.clear (); read_vrts = true; scene_build.AddMesh (); return lp_ReadLine (); }
The sl_Parse() method is called as long as it returns true.
bool CGEOLoader::sl_Parse ( 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; }
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 () { 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; }
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 () { Message ("common", 2020); MessageArg (1, "GEO supports geometry from a single layer, and no texturing."); }
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 () { 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; }
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 () { 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 (); }
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 () { 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; } } }
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 () { float vec[3]; PntPosition (vec); lf_Output (vec[0]); lf_Output (vec[1]); lf_Output (vec[2]); lf_Break (); pnt_index[PntID ()] = pnt_count++; }