// MonoGame - Copyright (C) The MonoGame Team // This file is subject to the terms and conditions defined in // file 'LICENSE.txt', which is part of this source code package. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Reflection; namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics { /// /// Provides methods and properties for managing a list of vertex data channels. /// public sealed class VertexChannelCollection : IList, ICollection, IEnumerable, IEnumerable { List channels; VertexContent vertexContent; /// /// Gets the number of vertex channels in the collection. /// public int Count { get { return channels.Count; } } /// /// Gets or sets the vertex channel at the specified index position. /// public VertexChannel this[int index] { get { return channels[index]; } set { channels[index] = value; } } /// /// Gets or sets the vertex channel with the specified name. /// public VertexChannel this[string name] { get { var index = IndexOf(name); if (index < 0) throw new ArgumentException("name"); return channels[index]; } set { var index = IndexOf(name); if (index < 0) throw new ArgumentException("name"); channels[index] = value; } } /// /// Determines whether the collection is read-only. /// bool ICollection.IsReadOnly { get { return false; } } /// /// Creates an instance of VertexChannelCollection. /// /// The VertexContent object that owns this collection. internal VertexChannelCollection(VertexContent vertexContent) { this.vertexContent = vertexContent; channels = new List(); _insertOverload = GetType().GetMethods().First(m => m.Name == "Insert" && m.IsGenericMethodDefinition); } /// /// Adds a new vertex channel to the end of the collection. /// /// Type of the channel. /// Name of the new channel. /// Initial data for the new channel. If null, the channel is filled with the default value for that type. /// The newly added vertex channel. public VertexChannel Add(string name, IEnumerable channelData) { return Insert(channels.Count, name, channelData); } /// /// Adds a new vertex channel to the end of the collection. /// /// Name of the new channel. /// Type of data to be contained in the new channel. /// Initial data for the new channel. If null, the channel is filled with the default value for that type. /// The newly added vertex channel. public VertexChannel Add(string name, Type elementType, IEnumerable channelData) { return Insert(channels.Count, name, elementType, channelData); } /// /// Removes all vertex channels from the collection. /// public void Clear() { channels.Clear(); } /// /// Determines whether the collection contains the specified vertex channel. /// /// Name of the channel being searched for. /// true if the channel was found; false otherwise. public bool Contains(string name) { return channels.Exists(c => { return c.Name == name; }); } /// /// Determines whether the collection contains the specified vertex channel. /// /// The channel being searched for. /// true if the channel was found; false otherwise. public bool Contains(VertexChannel item) { return channels.Contains(item); } /// /// Converts the channel, at the specified index, to another vector format. /// /// Type of the target format. Can be one of the following: Single, Vector2, Vector3, Vector4, IPackedVector /// Index of the channel to be converted. /// New channel in the specified format. public VertexChannel ConvertChannelContent(int index) { if (index < 0 || index >= channels.Count) throw new ArgumentOutOfRangeException("index"); // Get the channel at that index var channel = this[index]; // Remove it because we cannot add a new channel with the same name RemoveAt(index); VertexChannel result = null; try { // Insert a new converted channel at the same index result = Insert(index, channel.Name, channel.ReadConvertedContent()); } catch { // If anything went wrong, put the old channel back... channels.Insert(index, channel); // ...before throwing the exception again throw; } // Return the new converted channel return result; } /// /// Converts the channel, specified by name to another vector format. /// /// Type of the target format. Can be one of the following: Single, Vector2, Vector3, Vector4, IPackedVector /// Name of the channel to be converted. /// New channel in the specified format. public VertexChannel ConvertChannelContent(string name) { var index = IndexOf(name); if (index < 0) throw new ArgumentException("name"); return ConvertChannelContent(index); } /// /// Gets the vertex channel with the specified index and content type. /// /// Type of a vertex channel. /// Index of a vertex channel. /// The vertex channel. public VertexChannel Get(int index) { if (index < 0 || index >= channels.Count) throw new ArgumentOutOfRangeException("index"); var channel = this[index]; // Make sure the channel type is as expected if (channel.ElementType != typeof(T)) throw new InvalidOperationException("Mismatched channel type"); return (VertexChannel)channel; } /// /// Gets the vertex channel with the specified name and content type. /// /// Type of the vertex channel. /// Name of a vertex channel. /// The vertex channel. public VertexChannel Get(string name) { var index = IndexOf(name); if (index < 0) throw new ArgumentException("name"); return Get(index); } /// /// Gets an enumerator that iterates through the vertex channels of a collection. /// /// Enumerator for the collection. public IEnumerator GetEnumerator() { return channels.GetEnumerator(); } /// /// Determines the index of a vertex channel with the specified name. /// /// Name of the vertex channel being searched for. /// Index of the vertex channel. public int IndexOf(string name) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); return channels.FindIndex((v) => v.Name == name); } /// /// Determines the index of the specified vertex channel. /// /// Vertex channel being searched for. /// Index of the vertex channel. public int IndexOf(VertexChannel item) { if (item == null) throw new ArgumentNullException("item"); return channels.IndexOf(item); } /// /// Inserts a new vertex channel at the specified position. /// /// Type of the new channel. /// Index for channel insertion. /// Name of the new channel. /// The new channel. /// The inserted vertex channel. public VertexChannel Insert(int index, string name, IEnumerable channelData) { if ((index < 0) || (index > channels.Count)) throw new ArgumentOutOfRangeException("index"); if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); // Don't insert a channel with the same name if (IndexOf(name) >= 0) throw new ArgumentException("Vertex channel with name " + name + " already exists"); var channel = new VertexChannel(name); if (channelData != null) { // Insert the values from the enumerable into the channel channel.InsertRange(0, channelData); // Make sure we have the right number of vertices if (channel.Count != vertexContent.VertexCount) throw new ArgumentOutOfRangeException("channelData"); } else { // Insert enough default values to fill the channel channel.InsertRange(0, new ElementType[vertexContent.VertexCount]); } channels.Insert(index, channel); return channel; } // this reference the above Insert method and is initialized in the constructor private readonly MethodInfo _insertOverload; /// /// Inserts a new vertex channel at the specified position. /// /// Index for channel insertion. /// Name of the new channel. /// Type of the new channel. /// Initial data for the new channel. If null, it is filled with the default value. /// The inserted vertex channel. public VertexChannel Insert(int index, string name, Type elementType, IEnumerable channelData) { // Call the generic version of this method return (VertexChannel) _insertOverload.MakeGenericMethod(elementType).Invoke(this, new object[] { index, name, channelData }); } /// /// Removes the specified vertex channel from the collection. /// /// Name of the vertex channel being removed. /// true if the channel was removed; false otherwise. public bool Remove(string name) { var index = IndexOf(name); if (index >= 0) { channels.RemoveAt(index); return true; } return false; } /// /// Removes the specified vertex channel from the collection. /// /// The vertex channel being removed. /// true if the channel was removed; false otherwise. public bool Remove(VertexChannel item) { return channels.Remove(item); } /// /// Removes the vertex channel at the specified index position. /// /// Index of the vertex channel being removed. public void RemoveAt(int index) { channels.RemoveAt(index); } /// /// Adds a new vertex channel to the collection. /// /// Vertex channel to be added. void ICollection.Add(VertexChannel item) { channels.Add(item); } /// /// Copies the elements of the collection to an array, starting at the specified index. /// /// The destination array. /// The index at which to begin copying elements. void ICollection.CopyTo(VertexChannel[] array, int arrayIndex) { channels.CopyTo(array, arrayIndex); } /// /// Inserts an item at the specified index. /// /// The zero-based index at which item should be inserted. /// The item to insert. void IList.Insert(int index, VertexChannel item) { channels.Insert(index, item); } /// /// Returns an enumerator that iterates through a collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return channels.GetEnumerator(); } } }