Пример #1
0
 /// <summary>
 /// 立方体の GeometricPrimitive を生成します。
 /// </summary>
 /// <param name="size"></param>
 /// <returns>生成された立方体の GeometricPrimitive。</returns>
 GeometricPrimitive CreateCubePrimitive(float size)
 {
     var cube = new Cube { Size = size };
     var source = new VertexSource<VertexPositionNormal, ushort>();
     cube.Make(source);
     return GeometricPrimitive.Create(graphicsDevice, source);
 }
Пример #2
0
 public void rewind(int path_id)
 {
     VertexSource.rewind(path_id);
     lastX = 0.0;
     lastY = 0.0;
     m_curve3.reset();
     m_curve4.reset();
 }
Пример #3
0
        Tuple <VertexChannel, VertexChannel> GenerateJointChannels(Vector4[] jointIndices, Vector3[] jointWeights, int[] positionIndices)
        {
            if (jointIndices.Length == 0)
            {
                return(null);
            }

            VertexSource indexSource  = new VertexSource();
            VertexSource weightSource = new VertexSource();

            indexSource.Stride = 4;
            indexSource.Data   = new float[jointIndices.Length * 4];

            weightSource.Stride = 3;
            weightSource.Data   = new float[jointWeights.Length * 3];

            for (int i = 0; i < jointIndices.Length; i++)
            {
                indexSource.Data[i * 4 + 0] = jointIndices[i].X;
                indexSource.Data[i * 4 + 1] = jointIndices[i].Y;
                indexSource.Data[i * 4 + 2] = jointIndices[i].Z;
                indexSource.Data[i * 4 + 3] = jointIndices[i].W;
            }

            for (int i = 0; i < jointWeights.Length; i++)
            {
                weightSource.Data[i * 3 + 0] = jointWeights[i].X;
                weightSource.Data[i * 3 + 1] = jointWeights[i].Y;
                weightSource.Data[i * 3 + 2] = jointWeights[i].Z;
            }

            VertexElement indexDesc = new VertexElement();

            indexDesc.Offset              = 0;
            indexDesc.UsageIndex          = 0;
            indexDesc.VertexElementFormat = VertexElementFormat.Vector4;
            indexDesc.VertexElementUsage  = VertexElementUsage.BlendIndices;

            VertexElement weightDesc = new VertexElement();

            weightDesc.Offset              = 0;
            weightDesc.UsageIndex          = 0;
            weightDesc.VertexElementFormat = VertexElementFormat.Vector3;
            weightDesc.VertexElementUsage  = VertexElementUsage.BlendWeight;

            VertexChannel indexChannel  = new VertexChannel(indexSource, indexDesc);
            VertexChannel weightChannel = new VertexChannel(weightSource, weightDesc);

            indexChannel.Indices  = new int[positionIndices.Length];
            weightChannel.Indices = new int[positionIndices.Length];

            Array.Copy(positionIndices, indexChannel.Indices, positionIndices.Length);
            Array.Copy(positionIndices, weightChannel.Indices, positionIndices.Length);

            return(new Tuple <VertexChannel, VertexChannel>(indexChannel, weightChannel));
        }
Пример #4
0
        /// <summary>
        /// 立方体の GeometricPrimitive を生成します。
        /// </summary>
        /// <param name="size"></param>
        /// <returns>生成された立方体の GeometricPrimitive。</returns>
        GeometricPrimitive CreateCubePrimitive(float size)
        {
            var cube = new Cube {
                Size = size
            };
            var source = new VertexSource <VertexPositionNormal, ushort>();

            cube.Make(source);
            return(GeometricPrimitive.Create(graphicsDevice, source));
        }
Пример #5
0
        /// <summary>
        /// VertexSource へ頂点データを設定します。
        /// </summary>
        /// <param name="source">頂点データが設定される VertexSource。</param>
        public void Make(VertexSource<VertexPositionNormalColor, ushort> source)
        {
            source.AddIndex((ushort) (source.CurrentVertex + 0));
            source.AddIndex((ushort) (source.CurrentVertex + 1));
            source.AddIndex((ushort) (source.CurrentVertex + 2));
            source.AddIndex((ushort) (source.CurrentVertex + 0));
            source.AddIndex((ushort) (source.CurrentVertex + 2));
            source.AddIndex((ushort) (source.CurrentVertex + 3));

            source.AddVertex(new VertexPositionNormalColor(Position0, Normal, Color));
            source.AddVertex(new VertexPositionNormalColor(Position1, Normal, Color));
            source.AddVertex(new VertexPositionNormalColor(Position2, Normal, Color));
            source.AddVertex(new VertexPositionNormalColor(Position3, Normal, Color));
        }
Пример #6
0
        public ShapePath.FlagsAndCommand vertex(out double x, out double y)
        {
            if (!ShapePath.is_stop(m_curve3.vertex(out x, out y)))
            {
                lastX = x;
                lastY = y;
                return(ShapePath.FlagsAndCommand.CommandLineTo);
            }

            if (!ShapePath.is_stop(m_curve4.vertex(out x, out y)))
            {
                lastX = x;
                lastY = y;
                return(ShapePath.FlagsAndCommand.CommandLineTo);
            }

            double ct2_x;
            double ct2_y;
            double end_x;
            double end_y;

            ShapePath.FlagsAndCommand cmd = VertexSource.vertex(out x, out y);
            switch (cmd)
            {
            case ShapePath.FlagsAndCommand.CommandCurve3:
                VertexSource.vertex(out end_x, out end_y);

                m_curve3.init(lastX, lastY, x, y, end_x, end_y);

                m_curve3.vertex(out x, out y);                            // First call returns path_cmd_move_to
                m_curve3.vertex(out x, out y);                            // This is the first vertex of the curve
                cmd = ShapePath.FlagsAndCommand.CommandLineTo;
                break;

            case ShapePath.FlagsAndCommand.CommandCurve4:
                VertexSource.vertex(out ct2_x, out ct2_y);
                VertexSource.vertex(out end_x, out end_y);

                m_curve4.init(lastX, lastY, x, y, ct2_x, ct2_y, end_x, end_y);

                m_curve4.vertex(out x, out y);                            // First call returns path_cmd_move_to
                m_curve4.vertex(out x, out y);                            // This is the first vertex of the curve
                cmd = ShapePath.FlagsAndCommand.CommandLineTo;
                break;
            }
            lastX = x;
            lastY = y;
            return(cmd);
        }
Пример #7
0
            /// <summary>
            /// 立方体の GeometricPrimitive を生成します。
            /// </summary>
            /// <returns>生成された立方体の GeometricPrimitive。</returns>
            GeometricPrimitive CreateCubePrimitive()
            {
                var cube = new Cube
                {
                    Size          = 1,
                    BackwardColor = Color.Blue,
                    ForwardColor  = Color.BlueViolet,
                    RightColor    = Color.OrangeRed,
                    LeftColor     = Color.Red,
                    UpColor       = Color.Green,
                    DownColor     = Color.GreenYellow
                };
                var source = new VertexSource <VertexPositionNormalColor, ushort>();

                cube.Make(source);
                return(Graphics.GeometricPrimitive.Create(Screen.GraphicsDevice, source));
            }
Пример #8
0
        VertexElementFormat GetVertexElementFormat(VertexSource source, String semantic)
        {
            switch (semantic)
            {
            case "VERTEX":
            case "NORMAL":
            case "TEXCOORD":
            case "TEXTANGENT":
            case "TEXBINORMAL":
                if (source.Stride == 2)
                {
                    return(VertexElementFormat.Vector2);
                }
                else if (source.Stride == 3)
                {
                    return(VertexElementFormat.Vector3);
                }
                else
                {
                    return(VertexElementFormat.Vector4);
                }

            case "COLOR":
                if (source.Stride == 3)
                {
                    return(VertexElementFormat.Vector3);
                }
                else if (source.Stride == 4)
                {
                    return(VertexElementFormat.Vector4);
                }
                else if (source.Stride == 1)
                {
                    return(VertexElementFormat.Single);
                }
                else
                {
                    throw new Exception("Unsupported Color format");
                }

            default:
                throw new Exception("Unknown vertex element format");
            }
        }
Пример #9
0
        /// <summary>
        /// Parses all the source elements of the current mesh and returns
        /// the list of all found vertex sources.
        /// </summary>
        /// <param name="xmlMeshNode">XML Mesh node</param>
        /// <returns>List of found vertex sources</returns>
        protected List <VertexSource> ReadSources(XmlNode xmlMeshNode)
        {
            List <VertexSource> vertexSources = new List <VertexSource>();

            XmlNodeList nodes = xmlMeshNode.SelectNodes("source");

            if (nodes.Count == 0)
            {
                return(vertexSources);
            }

            foreach (XmlNode node in nodes)
            {
                Source       source = new Source(node, true);
                VertexSource v      = new VertexSource();
                v.Stride = source.Stride;
                v.Data   = (float[])source.Data;

                node.TryGetAttribute("id", out v.GlobalID);
                vertexSources.Add(v);
            }

            return(vertexSources);
        }
Пример #10
0
        /// <summary>
        /// InterBlockMesh を生成します。
        /// </summary>
        /// <param name="lodBlocks">各 LOD の InterBlock を要素とした配列。</param>
        /// <returns>生成された BlockMesh。</returns>
        static InterBlockMesh Create(InterBlock[] lodBlocks)
        {
            // InterBlockMesh を生成します。
            var mesh = new InterBlockMesh();

            // InterBlockEffect を生成します。
            // LOD 間で Material は共有しているので、最大 LOD の Material から生成します。
            mesh.MeshMaterials = new BlockMeshMaterial[lodBlocks[0].Materials.Count];
            for (int i = 0; i < mesh.MeshMaterials.Length; i++)
            {
                var block = lodBlocks[0];

                mesh.MeshMaterials[i] = new BlockMeshMaterial
                {
                    DiffuseColor = block.Materials[i].DiffuseColor.ToVector3(),
                    EmissiveColor = block.Materials[i].EmissiveColor.ToVector3(),
                    SpecularColor = block.Materials[i].SpecularColor.ToVector3(),
                    SpecularPower = block.Materials[i].SpecularPower
                };
            }

            // 実際に必要となる LOD 数をもとめます。
            int actualLodCount = 0;
            for (int lod = 0; lod < lodBlocks.Length; lod++)
            {
                // 要素数 0 の InterBlock は、それ以上粒度を荒くできなかったことを表します。
                if (lodBlocks[lod].Elements.Count == 0) break;

                actualLodCount++;
            }

            // 実際の LOD 数の分だけ InterBlockMeshLod 領域を確保します。
            mesh.MeshLods = new InterBlockMeshLod[actualLodCount];

            var meshPartVS = new VertexSource<VertexPositionNormal, ushort>();

            // LOD ごとに InterBlockMeshPart を生成します。
            for (int lod = 0; lod < actualLodCount; lod++)
            {
                var block = lodBlocks[lod];

                // Element を分類します。
                var elementClassifier = ElementClassifier.Classify(block.Elements);

                var cubeSurfaceVS = cubeSurfaceVertexSourceMap[block.ElementSize];

                int meshPartCount = elementClassifier.Parts.Count;
                var meshLod = new InterBlockMeshLod
                {
                    MeshParts = new InterBlockMeshPart[meshPartCount]
                };
                mesh.MeshLods[lod] = meshLod;

                // InterBlockMeshPart を生成して登録します。
                for (int i = 0; i < meshPartCount; i++)
                {
                    var part = elementClassifier.Parts[i];

                    // 頂点データを作成します。
                    meshPartVS.Clear();
                    MakeMeshPartVertexSource(meshPartVS, part, cubeSurfaceVS, block.ElementSize);

                    // InterBlockMeshPart を生成します。
                    meshLod.MeshParts[i] = new InterBlockMeshPart
                    {
                        MeshMaterialIndex = part.MaterialIndex,
                        Vertices = meshPartVS.Vertices.ToArray(),
                        Indices = meshPartVS.Indices.ToArray()
                    };
                }
            }

            return mesh;
        }
Пример #11
0
        /// <summary>
        /// InterBlockMesh を生成します。
        /// </summary>
        /// <param name="lodBlocks">各 LOD の InterBlock を要素とした配列。</param>
        /// <returns>生成された BlockMesh。</returns>
        static InterBlockMesh Create(InterBlock[] lodBlocks)
        {
            // InterBlockMesh を生成します。
            var mesh = new InterBlockMesh();

            // InterBlockEffect を生成します。
            // LOD 間で Material は共有しているので、最大 LOD の Material から生成します。
            mesh.MeshMaterials = new BlockMeshMaterial[lodBlocks[0].Materials.Count];
            for (int i = 0; i < mesh.MeshMaterials.Length; i++)
            {
                var block = lodBlocks[0];

                mesh.MeshMaterials[i] = new BlockMeshMaterial
                {
                    DiffuseColor  = block.Materials[i].DiffuseColor.ToVector3(),
                    EmissiveColor = block.Materials[i].EmissiveColor.ToVector3(),
                    SpecularColor = block.Materials[i].SpecularColor.ToVector3(),
                    SpecularPower = block.Materials[i].SpecularPower
                };
            }

            // 実際に必要となる LOD 数をもとめます。
            int actualLodCount = 0;

            for (int lod = 0; lod < lodBlocks.Length; lod++)
            {
                // 要素数 0 の InterBlock は、それ以上粒度を荒くできなかったことを表します。
                if (lodBlocks[lod].Elements.Count == 0)
                {
                    break;
                }

                actualLodCount++;
            }

            // 実際の LOD 数の分だけ InterBlockMeshLod 領域を確保します。
            mesh.MeshLods = new InterBlockMeshLod[actualLodCount];

            var meshPartVS = new VertexSource <VertexPositionNormal, ushort>();

            // LOD ごとに InterBlockMeshPart を生成します。
            for (int lod = 0; lod < actualLodCount; lod++)
            {
                var block = lodBlocks[lod];

                // Element を分類します。
                var elementClassifier = ElementClassifier.Classify(block.Elements);

                var cubeSurfaceVS = cubeSurfaceVertexSourceMap[block.ElementSize];

                int meshPartCount = elementClassifier.Parts.Count;
                var meshLod       = new InterBlockMeshLod
                {
                    MeshParts = new InterBlockMeshPart[meshPartCount]
                };
                mesh.MeshLods[lod] = meshLod;

                // InterBlockMeshPart を生成して登録します。
                for (int i = 0; i < meshPartCount; i++)
                {
                    var part = elementClassifier.Parts[i];

                    // 頂点データを作成します。
                    meshPartVS.Clear();
                    MakeMeshPartVertexSource(meshPartVS, part, cubeSurfaceVS, block.ElementSize);

                    // InterBlockMeshPart を生成します。
                    meshLod.MeshParts[i] = new InterBlockMeshPart
                    {
                        MeshMaterialIndex = part.MaterialIndex,
                        Vertices          = meshPartVS.Vertices.ToArray(),
                        Indices           = meshPartVS.Indices.ToArray()
                    };
                }
            }

            return(mesh);
        }
Пример #12
0
        /// <summary>
        /// Creates a vertex container from a set of "raw" vertex channels as read from the COLLADA file.
        /// Hence, it is assumed that each channel uses its own source (rather than every channel using
        /// the same single source). 
        /// </summary>
        /// <param name="inputChannels">Original Input Channels from COLLADA file</param>
        public VertexContainer(List<VertexChannel> inputChannels)
        {
            // Check for basic requirements
            if (inputChannels.Any(c => c.Description.VertexElementUsage == VertexElementUsage.Position) == false)
                throw new ArgumentException("Geometry has not all needed information. At least Positions are necessary!");

            // Convert Colors to single values, if necessary
            ConvertColorChannels(inputChannels);

            // Number of floats per vertex
            _vertexSize = CalculateVertexSize(inputChannels);

            // Expected number of indices
            int numIndices = inputChannels.First().Indices.Length;

            // vertex buffer with an expected number of 3/4 of the number of indices
            List<float> vbuffer = new List<float>(numIndices * 3 / 4);

            // Remember the position of distinct vertices to avoid duplicates
            Dictionary<VertexKey, int> usedVertices = new Dictionary<VertexKey, int>(numIndices * 3 / 4);

            // Indices referencing the new vertex buffer (vbuffer)
            List<int> indexList = new List<int>(numIndices);

            // Go through all indices to create vertices
            for (int i = 0; i < numIndices; i++)
            {
                VertexKey key = new VertexKey(inputChannels, i);
                int usedIndex = 0;

                if (usedVertices.TryGetValue(key, out usedIndex))
                {
                    // This vertex was already used, its index is "usedIndex"
                    indexList.Add(usedIndex);
                }
                else
                {
                    // If the vertex is unknown, add it to the vertex container (channel-wise)
                    // and remember that is has been used and the corresponding index
                    int index = vbuffer.Count / _vertexSize;

                    // Add all elements of the current vertex to the vertex buffer
                    foreach (VertexChannel channel in inputChannels)
                    {
                        float[] elementData = new float[channel.Source.Stride];
                        channel.GetValue(i, ref elementData, 0);

                        // origin of texture coordinates in XNA is top left, while
                        // in COLLADA it is bottom left. Therefore they need to be
                        // converted here
                        if (channel.Description.VertexElementUsage == VertexElementUsage.TextureCoordinate)
                        {
                            elementData[1] = 1 - elementData[1];
                        }

                        vbuffer.AddRange(elementData);
                    }

                    // Remember that this vertex combination was used before
                    // and store the index where it can be found in the
                    // vertex container
                    usedVertices.Add(key, index);

                    // Add reference to the just created vertex to the index list / buffer
                    indexList.Add(index);
                }
            }

            // Create adequate vertex channels
            int offset = 0;

            foreach (VertexChannel inputChannel in inputChannels)
            {
                VertexSource newSource = new VertexSource()
                {
                    Offset = offset // the element-offset within the vertex buffer
                };

                VertexElement desc = new VertexElement(offset, inputChannel.Description.VertexElementFormat,
                    inputChannel.Description.VertexElementUsage, inputChannel.Description.UsageIndex);

                VertexChannel newChannel = new VertexChannel(newSource, desc);
                _vertexChannels.Add(newChannel);

                offset += inputChannel.Source.Stride;
            }

            // Swap winding order
            for (int i = 0; i < indexList.Count; i += 3)
            {
                int swap = indexList[i + 1];
                indexList[i + 1] = indexList[i + 2];
                indexList[i + 2] = swap;
            }

            _data = vbuffer.ToArray();
            _indices = indexList.ToArray();

            // Update Source Data reference off all vertex channels
            foreach (VertexChannel channel in _vertexChannels)
            {
                // Every channel uses the same source now (global vertex buffer)
                channel.Source.Data = _data;

                // Every channel also uses the same indices
                channel.Indices = _indices;

                // The stride of one entry containing all elements for one vertex
                channel.Source.Stride = _vertexSize;
            }
        }
Пример #13
0
        /// <summary>
        /// Converts input channels that are used for colors from Vector3/Vector4
        /// to single float format, if necessary.
        /// </summary>
        /// <param name="inputChannels">Vertex Channels as read from COLLADA file</param>
        private void ConvertColorChannels(List<VertexChannel> inputChannels)
        {
            foreach (VertexChannel channel in inputChannels)
            {
                var usage = channel.Description.VertexElementUsage;
                var format = channel.Description.VertexElementFormat;

                if (usage != VertexElementUsage.Color) continue; // only relevant for colors
                if (format == VertexElementFormat.Single) continue; // nothing to do

                // Create updated vertex element description where each element is a single
                VertexElement newDesc = new VertexElement()
                {
                    Offset = 0,
                    UsageIndex = channel.Description.UsageIndex,
                    VertexElementFormat = VertexElementFormat.Single,
                    VertexElementUsage = VertexElementUsage.Color
                };

                // Old stride is 3 or 4 (corresponding to Vector3 or Vector4)
                int oldStride = channel.Source.Stride;
                float[] oldData = channel.Source.Data;

                // Create new source where each color is only represented by one single
                VertexSource newSource = new VertexSource();
                newSource.Stride = 1; // one float per color
                newSource.Data = new float[oldData.Length / oldStride];

                for (int i = 0; i < newSource.Data.Length; i++)
                {
                    // project start index to old data set (with $oldStride components per color)
                    int j = i * oldStride;

                    // Construct color from three or four floats in range [0,1]
                    Color color = (oldStride == 3) ?
                        new Color(oldData[j + 0], oldData[j + 1], oldData[j + 2]) :
                        new Color(oldData[j + 0], oldData[j + 1], oldData[j + 2], oldData[j + 3]);

                    newSource.Data[i] = ConvertColorToSingle(color);
                }

                // Update description and source of channel
                channel.Description = newDesc;
                channel.Source = newSource;
            }
        }
Пример #14
0
        /// <summary>
        /// VertexSource へ頂点データを設定します。
        /// </summary>
        /// <param name="source">頂点データが設定される VertexSource。</param>
        public void Make(VertexSource<VertexPositionNormalColor, ushort> source)
        {
            EnsureColors();

            var quad = new Quadrangle();

            for (int i = 0; i < normals.Length; i++)
            {
                ResolvePositionNormal(ref quad, ref normals[i]);
                quad.Color = colors[i];
                quad.Make(source);
            }
        }
Пример #15
0
        public IEnumerable <VertexData> Vertices()
        {
            VertexData lastPosition = new VertexData();

            IEnumerator <VertexData> vertexDataEnumerator = VertexSource.Vertices().GetEnumerator();

            while (vertexDataEnumerator.MoveNext())
            {
                VertexData vertexData = vertexDataEnumerator.Current;
                switch (vertexData.command)
                {
                case ShapePath.FlagsAndCommand.CommandCurve3:
                {
                    vertexDataEnumerator.MoveNext();
                    VertexData vertexDataEnd = vertexDataEnumerator.Current;
                    m_curve3.init(lastPosition.position.x, lastPosition.position.y, vertexData.position.x, vertexData.position.y, vertexDataEnd.position.x, vertexDataEnd.position.y);
                    IEnumerator <VertexData> curveIterator = m_curve3.Vertices().GetEnumerator();
                    curveIterator.MoveNext();                                     // First call returns path_cmd_move_to
                    do
                    {
                        curveIterator.MoveNext();
                        if (ShapePath.is_stop(curveIterator.Current.command))
                        {
                            break;
                        }
                        vertexData = new VertexData(ShapePath.FlagsAndCommand.CommandLineTo, curveIterator.Current.position);
                        yield return(vertexData);

                        lastPosition = vertexData;
                    } while (!ShapePath.is_stop(curveIterator.Current.command));
                }
                break;

                case ShapePath.FlagsAndCommand.CommandCurve4:
                {
                    vertexDataEnumerator.MoveNext();
                    VertexData vertexDataControl = vertexDataEnumerator.Current;
                    vertexDataEnumerator.MoveNext();
                    VertexData vertexDataEnd = vertexDataEnumerator.Current;
                    m_curve4.init(lastPosition.position.x, lastPosition.position.y, vertexData.position.x, vertexData.position.y, vertexDataControl.position.x, vertexDataControl.position.y, vertexDataEnd.position.x, vertexDataEnd.position.y);
                    IEnumerator <VertexData> curveIterator = m_curve4.Vertices().GetEnumerator();
                    curveIterator.MoveNext();                                     // First call returns path_cmd_move_to
                    while (!ShapePath.is_stop(vertexData.command))
                    {
                        curveIterator.MoveNext();
                        if (ShapePath.is_stop(curveIterator.Current.command))
                        {
                            break;
                        }
                        vertexData = new VertexData(ShapePath.FlagsAndCommand.CommandLineTo, curveIterator.Current.position);
                        yield return(vertexData);

                        lastPosition = vertexData;
                    }
                }
                break;

                default:
                    yield return(vertexData);

                    lastPosition = vertexData;
                    break;
                }
            }
        }
Пример #16
0
 /// <summary>
 /// 立方体の GeometricPrimitive を生成します。
 /// </summary>
 /// <returns>生成された立方体の GeometricPrimitive。</returns>
 GeometricPrimitive CreateCubePrimitive()
 {
     var cube = new Cube
     {
         Size = 1,
         BackwardColor = Color.Blue,
         ForwardColor = Color.BlueViolet,
         RightColor = Color.OrangeRed,
         LeftColor = Color.Red,
         UpColor = Color.Green,
         DownColor = Color.GreenYellow
     };
     var source = new VertexSource<VertexPositionNormalColor, ushort>();
     cube.Make(source);
     return Graphics.GeometricPrimitive.Create(Screen.GraphicsDevice, source);
 }
Пример #17
0
        /// <summary>
        /// Imports a piece of geometry, but does only support meshes, no NURBS.
        /// </summary>
        /// <param name="xmlGeometryNode">XML node of the geometry</param>
        /// <exception cref="Exception">Only works with meshes</exception>
        Mesh ImportGeometry(XmlNode xmlGeometryNode, ColladaModel model)
        {
            // Find the mesh node
            XmlNode xmlMeshNode = xmlGeometryNode.SelectSingleNode(".//mesh");

            if (xmlMeshNode == null)
            {
                throw new Exception("No supported geometry (Mesh) found");
            }

            // Determine number of mesh parts
            XmlNodeList xmlTriangles = xmlMeshNode.SelectNodes("triangles|polygons|polylist");

            if (xmlTriangles.Count == 0)
            {
                throw new Exception("No triangles found in mesh. Only triangles are supported");
            }

            if (xmlTriangles[0].Name != "triangles")
            {
                // If there are polygons or a polylist, check that all of them are triangles
                if (xmlTriangles[0].Attributes["vcount"] != null)
                {
                    var vcounts      = XmlUtil.ParseInts(xmlTriangles[0].Attributes["vcount"].Value);
                    var nonTriangles = vcounts.Where(count => count != 3);
                    if (nonTriangles.Any())
                    {
                        throw new Exception("Found polygon with " + nonTriangles.First() +
                                            " elements. Only triangles are supported");
                    }
                }
            }

            // Source data for this mesh used by all mesh parts.
            List <VertexSource> sources = ReadSources(xmlMeshNode);

            Vector4[] jointIndices;
            Vector3[] jointWeights;

            // Skinning Information, if available
            GetJointWeightsAndIndices(xmlMeshNode, model, out jointIndices, out jointWeights);

            if (sources.Count == 0)
            {
                throw new Exception("No data found");
            }

            //-------------------------------------------------------
            // Create Mesh
            //-------------------------------------------------------
            Mesh mesh = new Mesh();

            mesh.Name      = XmlUtil.GetName(xmlGeometryNode);
            mesh.MeshParts = new MeshPart[xmlTriangles.Count];

            // A mesh container for every mesh part, since every mesh part may use different
            // vertex types. This can be optimized in the content processor, if needed
            mesh.VertexContainers = new VertexContainer[xmlTriangles.Count];

            string[] semantics = new string[] { "VERTEX", "COLOR", "NORMAL", "TEXCOORD", "TEXTANGENT", "TEXBINORMAL" };

            //-------------------------------------------------------
            // Create Mesh Parts
            //-------------------------------------------------------
            for (int i = 0; i < xmlTriangles.Count; i++)
            {
                XmlNode    xmlPart      = xmlTriangles[i];
                int        numTriangles = int.Parse(xmlPart.Attributes["count"].Value);
                List <int> indexStream  = new List <int>(numTriangles * 3);

                var pNodes = xmlPart.SelectNodes("p");
                if (pNodes.Count > 1)
                {
                    // Indices are scattered among numTriangles <p> tags
                    foreach (XmlNode p in pNodes)
                    {
                        indexStream.AddRange(XmlUtil.ParseInts(p.InnerText));
                    }
                }
                else
                {
                    // Indices are contained in one <p> tag
                    indexStream.AddRange(XmlUtil.ParseInts(pNodes[0].InnerText));
                }

                int[]    indices = indexStream.ToArray();
                MeshPart part    = new MeshPart();

                try
                {
                    if (xmlPart.Attributes["material"] == null)
                    {
                        throw new Exception("no material attribute found");
                    }
                    part.MaterialName = FindMaterial(xmlGeometryNode, xmlPart.Attributes["material"].Value);
                }
                catch (Exception)
                {
                    // No Material found
                    part.MaterialName = null;
                }

                // Read Vertex Channels
                List <VertexChannel> vertexChannels = new List <VertexChannel>();

                foreach (String semantic in semantics)
                {
                    XmlNode input = xmlPart.SelectSingleNode(".//input[@semantic='" + semantic + "']");
                    if (input == null)
                    {
                        continue;                // no such vertex channel defined
                    }
                    int    offset;
                    String sourceId;

                    if (!input.TryGetAttribute("source", out sourceId))
                    {
                        throw new Exception("Referenced source of input with '" + semantic + "' semantic not found");
                    }
                    if (!input.TryGetAttribute("offset", out offset))
                    {
                        throw new Exception("No offset attribute of input with '" + semantic + "' semantic found");
                    }

                    sourceId = sourceId.Replace("#", "");
                    VertexSource source = sources.Where(s => s.GlobalID.Equals(sourceId)).FirstOrDefault();
                    if (source == null)
                    {
                        if (semantic.Equals("VERTEX"))
                        {
                            sourceId = xmlGeometryNode.SelectSingleNode(".//input[@semantic='POSITION']/@source").InnerText.Substring(1);
                            source   = sources.Where(s => s.GlobalID.Equals(sourceId)).FirstOrDefault();
                        }

                        if (source == null)
                        {
                            throw new Exception("Source '" + sourceId + "' not found");
                        }
                    }

                    VertexElement desc = new VertexElement();
                    desc.Offset              = offset;
                    desc.UsageIndex          = 0;
                    desc.VertexElementFormat = GetVertexElementFormat(source, semantic);
                    desc.VertexElementUsage  = GetVertexElementUsage(semantic);

                    VertexChannel channel = new VertexChannel(source, desc);
                    channel.CopyIndices(indices, offset, numTriangles * 3);
                    vertexChannels.Add(channel);
                }

                var jointChannels = GenerateJointChannels(jointIndices, jointWeights,
                                                          vertexChannels.Where(c => c.Description.VertexElementUsage ==
                                                                               VertexElementUsage.Position).First().Indices);

                if (jointChannels != null)
                {
                    vertexChannels.Add(jointChannels.Item1);
                    vertexChannels.Add(jointChannels.Item2);
                }

                part.Vertices = new VertexContainer(vertexChannels);
                part.Indices  = part.Vertices.Indices;

                mesh.VertexContainers[i] = part.Vertices;
                mesh.MeshParts[i]        = part;
            }

            return(mesh);
        }