// 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();
}
}
}