/// <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); }
public void rewind(int path_id) { VertexSource.rewind(path_id); lastX = 0.0; lastY = 0.0; m_curve3.reset(); m_curve4.reset(); }
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)); }
/// <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)); }
/// <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)); }
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); }
/// <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)); }
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"); } }
/// <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); }
/// <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; }
/// <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); }
/// <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; } }
/// <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; } }
/// <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); } }
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; } } }
/// <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); }
/// <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); }