Resumé

X3D Parser for WPF

Cristian Merighi () 0.00

During the development of a more scalable and reusable architecture for 3D objects in XAML and WPF, I put some parsers which allowed me to delegate the rendering of (complex) geometric solids and to code in a more readable and light way.
This article is obsolete. Some functionalities might not work anymore. Comments are disabled.

I'm trying to use XAML and Windows Presentation Foundation API for 3D objects the more scalable and reusable way.
To accomplish that I built an infrastructure of custom subclasses (Viewport3D, ModelVisual3D...) with some extra-features.
(I'll publish this architecture of mine in a future time, it's now just a stub).

I'm also enriching the whole thing with bunches of primitives - standard and extended - reachable either via XAML or runtime code.

Leading the path through this project, I've found useful to set a (Dependency)Property in my base Solid class (a ModelVisual3D subclass) to handle the geometry/material/transform rendering of this object retrieving info from a X3D file.
It's obviously veeeery cozy lean on 3D editing tools for a correct positioning of nodes, normal vectors and, most of all, texture coords!

The idea was to create a "X3D document" class delegated to parse a collection of GeometryModel3D object.

The core of the parsing is in the following code:

private void Parse()
{
    // the parsing result of the X3D file is a List of GeometryModel3D objects
    _Model3DCollection = new List<GeometryModel3D>();
    
    XmlNodeList geomNodes = x3d.SelectNodes("X3D/Scene/Transform/Transform/Shape/IndexedFaceSet");
    foreach (XmlNode node in geomNodes)
    {
        GeometryModel3D geom = new GeometryModel3D();
        geom.Geometry = ParseGeometry(node);
        geom.Material = ParseMaterial(node.ParentNode.SelectSingleNode("Appearance/Material"));
        geom.Transform = ParseTransform(node.ParentNode.ParentNode.ParentNode);
        _Model3DCollection.Add(geom);
    }
}


private MeshGeometry3D ParseGeometry(System.Xml.XmlNode node)
{
    MeshGeometry3D mesh = new MeshGeometry3D();
    // parsing X3D
    string[] coords = Pacem.Utilities.StringHelper.RemoveNewLines(
        node.SelectSingleNode("Coordinate").Attributes["point"].Value).Split(',');
    string[] triangles = StringHelper.RemoveNewLines(
        node.Attributes["coordIndex"].Value).Split(',');

    // trap the absense either of the normals or of the textureCoords
    bool hasNormals = node.SelectSingleNode("Normal") != null;
    string[] normal = null; string[] normalIndex = null;
    if (hasNormals)
    {
        // NORMALS
        normal = StringHelper.RemoveNewLines(
            node.SelectSingleNode("Normal").Attributes["vector"].Value).Trim().Split(',');
        normalIndex = StringHelper.RemoveNewLines(node.Attributes["normalIndex"].Value).Split(',');

    }
    bool hasTexture = node.SelectSingleNode("TextureCoordinate") != null;
    string[] texCoord = null; string[] texCoordIndex = null;
    if (hasTexture)
    {
        // TEXTURE COORDS
        texCoord = StringHelper.RemoveNewLines(
            node.SelectSingleNode("TextureCoordinate").Attributes["point"].Value).Trim().Split(',');
        texCoordIndex = StringHelper.RemoveNewLines(
            node.Attributes["texCoordIndex"].Value).Replace(
                ",,","," // because of a bug of my X3D exporter
            ).Trim().Split(',');
    }
    
    IFormatProvider format = System.Globalization.CultureInfo.InvariantCulture;
    
    for (int j = 0; j < triangles.Length - 1; j++)
    {                
        int value = int.Parse(triangles[j], format);
        if (value != -1)
        {
            string[] s_point = coords[value].Trim().Split(' ');
            mesh.Positions.Add(new Point3D(
                double.Parse(s_point[0], format), 
                double.Parse(s_point[1], format), 
                double.Parse(s_point[2], format)));
            if (hasNormals)
            {
                int ndx_normal = int.Parse(normalIndex[j], format);
                string[] s_vector = normal[ndx_normal].Trim().Split(' ');
                mesh.Normals.Add(new Vector3D(
                    double.Parse(s_vector[0], format),
                    double.Parse(s_vector[1], format),
                    double.Parse(s_vector[2], format)));
            }
            if (hasTexture)
            {
                int ndx_texture = int.Parse(texCoordIndex[j], format);
                string[] s_p = texCoord[ndx_texture].Trim().Split(' ');
                double x = double.Parse(s_p[0], format);
                double y = -double.Parse(s_p[1], format);
                mesh.TextureCoordinates.Add(new Point(x, y));
            }
            mesh.TriangleIndices.Add(mesh.Positions.Count - 1);
        }
    }

    return mesh;
}
This allows me to write very scalable XAML, this way:
<pacem:Solid SourceType="X3D" Source="../../Logo.x3d" x:Name="Pacem">
    <ModelVisual3D.Transform>
              <TranslateTransform3D OffsetX="2.8530" OffsetY="0" OffsetZ="-.9270" />
            </ModelVisual3D.Transform>
    <pacem:Solid.Material>
        <DiffuseMaterial>
                <DiffuseMaterial.Brush>
                  <ImageBrush ImageSource="E:\Cristian\WPF\PacemWPF\RedGlass.jpg" />
                </DiffuseMaterial.Brush>
              </DiffuseMaterial>
    </pacem:Solid.Material>
</pacem:Solid>
X3D to WPF

...but this is a story I'll discuss later!

zip file « download code

Take care. Bye.

feedback
 

Syndicate

Author

Cristian Merighi facebook twitter google+ youtube

Latest articles

Top rated

Archive

Where am I?

Author

Cristian Merighi facebook twitter google+ youtube

I'm now reading

Feeds