Click here to Skip to main content
15,883,883 members
Articles / Programming Languages / C#

Fast Interoperability of 2D Shapes between 3D Applications and Your Software

Rate me:
Please Sign up or sign in to vote.
4.20/5 (2 votes)
1 Sep 2009CPOL5 min read 14.2K   9   1
Fast interoperability of 2D shapes between 3D applications and your software

Preface

Wikipedia says that interoperability is “a property referring to the ability of diverse systems and organizations to work together (inter-operate)”. Regarding software, it says that interoperability is “the capability of different programs to exchange data via a common set of exchange formats”. This has always been a problem, especially when talking about software that manages 3D or 2D information, due to the volume and diversity of the information to be exchanged.

Why is That a Problem?

In 2D images, for instance, it has always been more or less clear what kind of information applications should share: 2D tables of colors, expressed in one format or another, but color or amount of light, after all. There are some ‘de-facto’ standards like TGA (coming from IBM) and others developed by institutions or committees, like JPEG (name coming from Joint Photographic Experts Group). Some formats try to include other information, like opacity (alpha channels), or to express the same information in other forms, like the so-called HDR (High Dynamic Range) images, which need to store lighting information in floating point.

3D is a completely different story. Mostly because while 2D imaging is a mature field (and a relatively simple one too), 3D visualization is still changing a lot form month to month, with new techniques and algorithms, that need new and complex information stored in files. In addition to that, there are several kind of 3D applications, with different needs too: 3D modeling packages, real-time applications, videogames, etc. All this stuff makes interoperability a hard issue, and sharing a scene from two different applications can be a nightmare, with un-recognized parameters, missing information, etc.

However, and despite the written above, there have been many tries to become certain file formats a standard. It is the case of the old 3DS (from AutoDesk), the X File Format (from Microsoft –DirectX-), or the newer FBX, which is the latest try from AutoDesk to build a standard 3D file format, and is also supported by XNA.

The 2D Shapes Case

It may seem something obvious, but 3D formats are very focused in 3D, and 2D shapes are frequently left apart. For instance, neither 3DS or the default FBX file formats support 2D shapes. And that’s a problem, as 2D shapes like splines are very useful for many things in our software: paths or trajectories for animation, A.I., etc.

What’s the Solution?

In fact, there are many solutions:

  1. Use a different file format that supports 2D shapes
  2. Use the FBX SDK to make your own exporters/importers
  3. Export 2D shapes as 3D objects (this is what 3DSMax does when using the 3DS file format, for instance).
  4. etc.

I guess the best option would be the second one: use the FBX format, which seems to be the next standard, and until support for 2D shapes is included on it, make your own exporters/importers. However, that would take quite a bunch of ours, learning to use the SDK, and making exporters and importers for the different 3D modeling packages round there. You should go this way, but if you do not have time enough, I´ll show you an easier solution here, using an existing file format.

Choosing the Proper File Format

There are several file formats which support 2D shapes, but not all of them are Open Formats. Among the usually supported by 3D modeling applications, we find two “mostly open” formats: the ASE file format (ASCII) and the OBJ file format, developed by WaveFront. Both of them would make it, but we will choose the second (OBJ) because the first one is a bit more complex, and because 3DSMax does not include an ASE importer by default (it comes with an exporter only).

The OBJ Parser (2D Shapes Only)

Please note: I won’t write here a full parser of the OBJ format. We will just include a few lines of code to read OBJ files which hold information of 2D splines.

The following class will read each 2D shape present in the OBJ file, and store the list of vertices in a Dictionary (keyed by the name of the shape). Please note that I have just tested this code with OBJ files exported from 3DS Max, storing 2D splines only. You can make your own tests and change the code as necessary. A call to “LoadFile” will fill the “mFoundSplines” collection with the information of the shapes found in the file.

Note: Some 3D applications, like 3DSMax, use axis coordinates with the “Z” pointing “up”, while others prefer coordinates with the “Y” pointing up. In my case, this second option is the default, so if you want Z to point up (to read or save files from/to 3DSMax), you should set the parameter “pSwapYZ” to true in the call to LoadFile.

C#
public class OBJFileParser
{
    public static Dictionary<string, DX.Vector3[]> mFoundSplines;
    private static System.IO.StreamReader mStrmReader;

    /// <summary>
    /// 
    /// </summary>
    public static void LoadFile(string pFullFileName, bool pSwapYZ)
    {
        try
        {
            mFoundSplines = new Dictionary<string, Microsoft.DirectX.Vector3[]>();
            mStrmReader = new System.IO.StreamReader(pFullFileName);
            while (!mStrmReader.EndOfStream)
            {
                string line = mStrmReader.ReadLine();
                if (line.StartsWith("# shape"))
                {
                    string[] parts = line.Split(' ');
                    if (parts.Length != 3)
                        continue;
                    ReadShape(parts[2], pSwapYZ);
                }
            }
        }
        finally { mStrmReader.Close(); }
    }
 
    /// <summary>
    /// 
    /// </summary>
    private static void ReadShape(string pShapeName, bool pSwapYZ)
    {
        List<DX.Vector3> vertices = new List<Microsoft.DirectX.Vector3>();

        while (!mStrmReader.EndOfStream)
        {
            string line = mStrmReader.ReadLine();

            // Skip lines starting with #
            if (line.StartsWith("#"))
                continue;

            // Read Vertex
            if (line.StartsWith("v"))
            {
                // Important !: There are two spaces (‘ ‘) between the 
                // "v" and the first component of the vector. Split will return 5
                // strings
                string[] parts = line.Split(' ');
                if (parts.Length != 5)
                    continue;

                if(pSwapYZ)
                    vertices.Add(new Microsoft.DirectX.Vector3
                    (float.Parse(parts[2]), float.Parse(parts[4]), float.Parse(parts[3])));
                else vertices.Add(new Microsoft.DirectX.Vector3
                (float.Parse(parts[2]), float.Parse(parts[3]), float.Parse(parts[4])));
            }

            // Shapes have a line starting with "g" (plus the name of the shape), 
            // and another one with what seems to be indices to vertices. 
            // In this case, we won´t read that info, so we will use the 
            // "g"-starting line to detect the end of a shape
            if (line.StartsWith("g"))
                break;
        }

        // Add Spline
        if (vertices.Count > 0)
                mFoundSplines.Add(pShapeName, vertices.ToArray());            
    }
}

The OBJ Saver (2D Shapes Only)

We will save now our Splines following the OBJ format, and making sure that 3DS Max is able to read it. To use it, you need to call each method like:

  • OBJFileSaver.StartNewFile(): To reset internal collections
  • OBJFileSaver.AddShape(): To add each shape you want to save
  • OBJFileSaver.SaveFile(): To finally save everything to disk.

Note: Some 3D applications, like 3DSMax, use axis coordinates with the “Z” pointing “up”, while others prefer coordinates with the “Y” pointing up. In my case, this second option is the default, so if you want Z to point up (to read or save files from/to 3DSMax), you should set the parameter “pSwapYZ” to true in the call to SaveFile.

C#
public class OBJFileSaver
{
    private static System.IO.StreamWriter mStrmWriter = null;
    private static Dictionary<string, DX.Vector3[]> mShapes;
 
    /// <summary>
    /// 
    /// </summary>
    /// <param name="pFullFileName"></param>
    public static void StartNewFile()
    {
        mShapes = new Dictionary<string, Microsoft.DirectX.Vector3[]>();
     }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="?"></param>
    /// <param name="pShape"></param>
    public static void AddShape(string pShapeName, DX.Vector3[] pShape)
    {
        mShapes.Add(pShapeName, pShape);
    }
 
    /// <summary>
    /// 
    /// </summary>
    public static void SaveFile(string pFullFileName, bool pSwapYZ)
    {
        try
        {
            mStrmWriter = new System.IO.StreamWriter(pFullFileName);
            int vertexIdx = 1;
            foreach (string name in mShapes.Keys)
            {
                WriteShape(name, vertexIdx, pSwapYZ);
                vertexIdx += mShapes[name].Length;
            }
        }
        finally
        {
            mStrmWriter.Close();
        }
    }

 
    /// <summary>
    /// 
    /// </summary>
    /// <param name="pName"></param>
    private static void WriteShape(string pShapeName, int pVertexStartIdx, bool pSwapYZ)
    {
        // Write header
        mStrmWriter.WriteLine("");
        mStrmWriter.WriteLine("#");
        mStrmWriter.WriteLine("# shape " + pShapeName);
        mStrmWriter.WriteLine("#");
        mStrmWriter.WriteLine("");
 
         // Write vertices. Important !!: Set two (2) spaces between "v" and X component of vertex.
        foreach (DX.Vector3 vec in mShapes[pShapeName])
        {
            if(pSwapYZ)
                mStrmWriter.WriteLine(string.Format("v  {0} {1} {2}", vec.X, vec.Z, vec.Y));
            else mStrmWriter.WriteLine(string.Format("v  {0} {1} {2}", vec.X, vec.Y, vec.Z));
        }
 
        // Write number of vertices
        mStrmWriter.WriteLine("# " + mShapes[pShapeName].Length + " vertices");
        mStrmWriter.WriteLine("");
 
        // Write the "g" line, with the name of the shape again
        mStrmWriter.WriteLine("g " + pShapeName);
 
        // Write what seems to be a trivial list of indices to vertices. 
        // It doesn´t re-start for each shape
        string indices = "l ";
        int vertexIdx = pVertexStartIdx;
        for (int i = 0; i < mShapes[pShapeName].Length; i++)
        {
            indices += vertexIdx + " ";
            vertexIdx++;
        }
        mStrmWriter.WriteLine(indices);
        mStrmWriter.WriteLine("");
        mStrmWriter.Flush();
    }
}

Limitations of This Class

Keep in mind that this class will only export the output geometry of your Splines (only vertex positions) and the name of the shape. Other parameters like Spline tension, continuity, tangents, etc. are not included. Any other information regarding 3DSMax or any other modeling application (like interpolation properties) will not be saved either.

Conclusion

Until more standard formats like FBX support 2D shapes (something that should happen soon), this is a very easy way to add support for 2D shapes to your applications. Hope you find it useful !

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Spain Spain
Inaki Ayucar is a Microsoft MVP in DirectX/XNA, and a software engineer involved in development since his first Spectrum 48k, in the year 1987. He is the founder and chief developer of The Simax Project (www.simaxvirt.com) and is very interested in DirectX/XNA, physics, game development, simulation, C++ and C#.

His blog is: http://graphicdna.blogspot.com

To contact Inaki: iayucar@simax.es

Comments and Discussions

 
GeneralMy vote of 5 Pin
Madhan Mohan Reddy P2-Nov-12 1:27
professionalMadhan Mohan Reddy P2-Nov-12 1:27 
useful..

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.