(ded4a3e0a) v0.9.0.7
This commit is contained in:
+54
@@ -0,0 +1,54 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class AlphaTestMaterialContent : MaterialContent
|
||||
{
|
||||
public const string AlphaKey = "Alpha";
|
||||
public const string AlphaFunctionKey = "AlphaFunction";
|
||||
public const string DiffuseColorKey = "DiffuseColor";
|
||||
public const string ReferenceAlphaKey = "ReferenceAlpha";
|
||||
public const string TextureKey = "Texture";
|
||||
public const string VertexColorEnabledKey = "VertexColorEnabled";
|
||||
|
||||
public float? Alpha
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(AlphaKey); }
|
||||
set { SetProperty(AlphaKey, value); }
|
||||
}
|
||||
|
||||
public CompareFunction? AlphaFunction
|
||||
{
|
||||
get { return GetValueTypeProperty<CompareFunction>(AlphaFunctionKey); }
|
||||
set { SetProperty(AlphaFunctionKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? DiffuseColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(DiffuseColorKey); }
|
||||
set { SetProperty(DiffuseColorKey, value); }
|
||||
}
|
||||
|
||||
public int? ReferenceAlpha
|
||||
{
|
||||
get { return GetValueTypeProperty<int>(ReferenceAlphaKey); }
|
||||
set { SetProperty(ReferenceAlphaKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture
|
||||
{
|
||||
get { return GetTexture(TextureKey); }
|
||||
set { SetTexture(TextureKey, value); }
|
||||
}
|
||||
|
||||
public bool? VertexColorEnabled
|
||||
{
|
||||
get { return GetValueTypeProperty<bool>(VertexColorEnabledKey); }
|
||||
set { SetProperty(VertexColorEnabledKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for maintaining an animation channel. An animation channel is a collection of keyframes describing the movement of a single bone or rigid object.
|
||||
/// </summary>
|
||||
public sealed class AnimationChannel : ICollection<AnimationKeyframe>, IEnumerable<AnimationKeyframe>, IEnumerable
|
||||
{
|
||||
List<AnimationKeyframe> keyframes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of keyframes in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return keyframes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keyframe at the specified index position.
|
||||
/// </summary>
|
||||
public AnimationKeyframe this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return keyframes[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the object is read-only.
|
||||
/// </summary>
|
||||
bool ICollection<AnimationKeyframe>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of AnimationChannel.
|
||||
/// </summary>
|
||||
public AnimationChannel()
|
||||
{
|
||||
keyframes = new List<AnimationKeyframe>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To satisfy ICollection
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
void ICollection<AnimationKeyframe>.Add(AnimationKeyframe item)
|
||||
{
|
||||
keyframes.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new keyframe to the collection, automatically sorting the contents according to keyframe times.
|
||||
/// </summary>
|
||||
/// <param name="item">Keyframe to be added to the channel.</param>
|
||||
/// <returns>Index of the new keyframe.</returns>
|
||||
public int Add(AnimationKeyframe item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException("item");
|
||||
|
||||
// Find the correct place at which to insert it, so we can know the index to return.
|
||||
// The alternative is Add, Sort then return IndexOf, which would be horribly inefficient
|
||||
// and the order returned by Sort would change each time for keyframes with the same time.
|
||||
|
||||
// BinarySearch returns the index of the first item found with the same time, or the bitwise
|
||||
// complement of the next largest item found.
|
||||
int index = keyframes.BinarySearch(item);
|
||||
if (index >= 0)
|
||||
{
|
||||
// If a match is found, we do not know if it is at the start, middle or end of a range of
|
||||
// keyframes with the same time value. So look for the end of the range and insert there
|
||||
// so we have deterministic behaviour.
|
||||
while (index < keyframes.Count)
|
||||
{
|
||||
if (item.CompareTo(keyframes[index]) != 0)
|
||||
break;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If BinarySearch returns a negative value, it is the bitwise complement of the next largest
|
||||
// item in the list. So we just do a bitwise complement and insert at that index.
|
||||
index = ~index;
|
||||
}
|
||||
keyframes.Insert(index, item);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all keyframes from the collection.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
keyframes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the collection for the specified keyframe.
|
||||
/// </summary>
|
||||
/// <param name="item">Keyframe being searched for.</param>
|
||||
/// <returns>true if the keyframe exists; false otherwise.</returns>
|
||||
public bool Contains(AnimationKeyframe item)
|
||||
{
|
||||
return keyframes.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To satisfy ICollection
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="arrayIndex"></param>
|
||||
void ICollection<AnimationKeyframe>.CopyTo(AnimationKeyframe[] array, int arrayIndex)
|
||||
{
|
||||
keyframes.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the index for the specified keyframe.
|
||||
/// </summary>
|
||||
/// <param name="item">Identity of a keyframe.</param>
|
||||
/// <returns>Index of the specified keyframe.</returns>
|
||||
public int IndexOf(AnimationKeyframe item)
|
||||
{
|
||||
return keyframes.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified keyframe from the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">Keyframe being removed.</param>
|
||||
/// <returns>true if the keyframe was removed; false otherwise.</returns>
|
||||
public bool Remove(AnimationKeyframe item)
|
||||
{
|
||||
return keyframes.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the keyframe at the specified index position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the keyframe being removed.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
keyframes.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the keyframes.
|
||||
/// </summary>
|
||||
/// <returns>Enumerator for the keyframe collection.</returns>
|
||||
public IEnumerator<AnimationKeyframe> GetEnumerator()
|
||||
{
|
||||
return keyframes.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To satisfy ICollection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return keyframes.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of animation data channels, one per bone or rigid object.
|
||||
/// </summary>
|
||||
public sealed class AnimationChannelDictionary : NamedValueDictionary<AnimationChannel>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of AnimationChannelDictionary.
|
||||
/// </summary>
|
||||
public AnimationChannelDictionary()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties for maintaining an animation.
|
||||
/// </summary>
|
||||
public class AnimationContent : ContentItem
|
||||
{
|
||||
AnimationChannelDictionary channels;
|
||||
TimeSpan duration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of animation data channels. Each channel describes the movement of a single bone or rigid object.
|
||||
/// </summary>
|
||||
public AnimationChannelDictionary Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total length of the animation.
|
||||
/// </summary>
|
||||
public TimeSpan Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return duration;
|
||||
}
|
||||
set
|
||||
{
|
||||
duration = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of AnimationContent.
|
||||
/// </summary>
|
||||
public AnimationContent()
|
||||
{
|
||||
channels = new AnimationChannelDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of named animations.
|
||||
/// </summary>
|
||||
public sealed class AnimationContentDictionary : NamedValueDictionary<AnimationContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of AnimationContentDictionary.
|
||||
/// </summary>
|
||||
public AnimationContentDictionary()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for managing a keyframe. A keyframe describes the position of an animation channel at a single point in time.
|
||||
/// </summary>
|
||||
public sealed class AnimationKeyframe : IComparable<AnimationKeyframe>
|
||||
{
|
||||
TimeSpan time;
|
||||
Matrix transform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time offset from the start of the animation to the position described by this keyframe.
|
||||
/// </summary>
|
||||
public TimeSpan Time
|
||||
{
|
||||
get
|
||||
{
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position described by this keyframe.
|
||||
/// </summary>
|
||||
public Matrix Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
set
|
||||
{
|
||||
transform = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of AnimationKeyframe with the specified time offsetand transform.
|
||||
/// </summary>
|
||||
/// <param name="time">Time offset of the keyframe.</param>
|
||||
/// <param name="transform">Position of the keyframe.</param>
|
||||
public AnimationKeyframe(TimeSpan time, Matrix transform)
|
||||
{
|
||||
this.time = time;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance of a keyframe to another.
|
||||
/// </summary>
|
||||
/// <param name="other">Keyframe being compared to.</param>
|
||||
/// <returns>Indication of their relative values.</returns>
|
||||
public int CompareTo(AnimationKeyframe other)
|
||||
{
|
||||
// No sense in comparing the transform, so compare the time.
|
||||
// This would be used for sorting keyframes in time order.
|
||||
return time.CompareTo(other.time);
|
||||
}
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
using ATI.TextureConverter;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public abstract class AtcBitmapContent : BitmapContent
|
||||
{
|
||||
internal byte[] _bitmapData;
|
||||
|
||||
public AtcBitmapContent()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public AtcBitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
public override byte[] GetPixelData()
|
||||
{
|
||||
return _bitmapData;
|
||||
}
|
||||
|
||||
public override void SetPixelData(byte[] sourceData)
|
||||
{
|
||||
_bitmapData = sourceData;
|
||||
}
|
||||
|
||||
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
SetPixelData(sourceBitmap.GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Destination region copy is not yet supported
|
||||
if (destinationRegion != new Rectangle(0, 0, Width, Height))
|
||||
return false;
|
||||
|
||||
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to full colour 32-bit format. Floating point would be preferred for processing, but it appears the ATICompressor does not support this
|
||||
var colorBitmap = new PixelBitmapContent<Color>(sourceRegion.Width, sourceRegion.Height);
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, colorBitmap, new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height));
|
||||
sourceBitmap = colorBitmap;
|
||||
|
||||
ATICompressor.CompressionFormat targetFormat;
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat.RgbaAtcExplicitAlpha:
|
||||
targetFormat = ATICompressor.CompressionFormat.AtcRgbaExplicitAlpha;
|
||||
break;
|
||||
case SurfaceFormat.RgbaAtcInterpolatedAlpha:
|
||||
targetFormat = ATICompressor.CompressionFormat.AtcRgbaInterpolatedAlpha;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
var sourceData = sourceBitmap.GetPixelData();
|
||||
var compressedData = ATICompressor.Compress(sourceData, Width, Height, targetFormat);
|
||||
SetPixelData(compressedData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (format == destinationFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
destinationBitmap.SetPixelData(GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other support for copying from a ATC texture yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class AtcExplicitBitmapContent : AtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of AtcExplicitBitmapContent.
|
||||
/// </summary>
|
||||
public AtcExplicitBitmapContent()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of AtcExplicitBitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public AtcExplicitBitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbaAtcExplicitAlpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "ATITC Explicit Alpha " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class AtcInterpolatedBitmapContent : AtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of AtcInterpolatedBitmapContent.
|
||||
/// </summary>
|
||||
public AtcInterpolatedBitmapContent()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of AtcInterpolatedBitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public AtcInterpolatedBitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbaAtcInterpolatedAlpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "ATITC Interpolated Alpha " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class BasicMaterialContent : MaterialContent
|
||||
{
|
||||
public const string AlphaKey = "Alpha";
|
||||
public const string DiffuseColorKey = "DiffuseColor";
|
||||
public const string EmissiveColorKey = "EmissiveColor";
|
||||
public const string SpecularColorKey = "SpecularColor";
|
||||
public const string SpecularPowerKey = "SpecularPower";
|
||||
public const string TextureKey = "Texture";
|
||||
public const string VertexColorEnabledKey = "VertexColorEnabled";
|
||||
|
||||
public float? Alpha
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(AlphaKey); }
|
||||
set { SetProperty(AlphaKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? DiffuseColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(DiffuseColorKey); }
|
||||
set { SetProperty(DiffuseColorKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? EmissiveColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(EmissiveColorKey); }
|
||||
set { SetProperty(EmissiveColorKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? SpecularColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(SpecularColorKey); }
|
||||
set { SetProperty(SpecularColorKey, value); }
|
||||
}
|
||||
|
||||
public float? SpecularPower
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(SpecularPowerKey); }
|
||||
set { SetProperty(SpecularPowerKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture
|
||||
{
|
||||
get { return GetTexture(TextureKey); }
|
||||
set { SetTexture(TextureKey, value); }
|
||||
}
|
||||
|
||||
public bool? VertexColorEnabled
|
||||
{
|
||||
get { return GetValueTypeProperty<bool>(VertexColorEnabledKey); }
|
||||
set { SetProperty(VertexColorEnabledKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties and methods for creating and maintaining a bitmap resource.
|
||||
/// </summary>
|
||||
public abstract class BitmapContent : ContentItem
|
||||
{
|
||||
int height;
|
||||
int width;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the bitmap, in pixels.
|
||||
/// </summary>
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
return height;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException("height");
|
||||
height = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the bitmap, in pixels.
|
||||
/// </summary>
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
return width;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException("width");
|
||||
width = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BitmapContent.
|
||||
/// </summary>
|
||||
protected BitmapContent()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BitmapContent with the specified width or height.
|
||||
/// </summary>
|
||||
/// <param name="width">Width, in pixels, of the bitmap resource.</param>
|
||||
/// <param name="height">Height, in pixels, of the bitmap resource.</param>
|
||||
protected BitmapContent(int width, int height)
|
||||
{
|
||||
// Write to properties so validation is run.
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies one bitmap into another.
|
||||
/// The destination bitmap can be in any format and size. If the destination is larger or smaller, the source bitmap is scaled accordingly.
|
||||
/// </summary>
|
||||
/// <param name="sourceBitmap">BitmapContent being copied.</param>
|
||||
/// <param name="destinationBitmap">BitmapContent being overwritten.</param>
|
||||
public static void Copy(BitmapContent sourceBitmap, BitmapContent destinationBitmap)
|
||||
{
|
||||
if (sourceBitmap == null)
|
||||
throw new ArgumentNullException("sourceBitmap");
|
||||
if (destinationBitmap == null)
|
||||
throw new ArgumentNullException("destinationBitmap");
|
||||
|
||||
var sourceRegion = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
|
||||
var destinationRegion = new Rectangle(0, 0, destinationBitmap.Width, destinationBitmap.Height);
|
||||
|
||||
Copy(sourceBitmap, sourceRegion, destinationBitmap, destinationRegion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies one bitmap into another.
|
||||
/// The destination bitmap can be in any format and size. If the destination is larger or smaller, the source bitmap is scaled accordingly.
|
||||
/// </summary>
|
||||
/// <param name="sourceBitmap">BitmapContent being copied.</param>
|
||||
/// <param name="sourceRegion">Region of sourceBitmap.</param>
|
||||
/// <param name="destinationBitmap">BitmapContent being overwritten.</param>
|
||||
/// <param name="destinationRegion">Region of bitmap to be overwritten.</param>
|
||||
public static void Copy(BitmapContent sourceBitmap, Rectangle sourceRegion, BitmapContent destinationBitmap, Rectangle destinationRegion)
|
||||
{
|
||||
ValidateCopyArguments(sourceBitmap, sourceRegion, destinationBitmap, destinationRegion);
|
||||
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
throw new InvalidOperationException("Could not retrieve surface format of source bitmap");
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
throw new InvalidOperationException("Could not retrieve surface format of destination bitmap");
|
||||
|
||||
// If the formats are the same and the regions are the full bounds of the bitmaps and they are the same size, do a simpler copy
|
||||
if (sourceFormat == destinationFormat && sourceRegion == destinationRegion
|
||||
&& sourceRegion == new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height)
|
||||
&& destinationRegion == new Rectangle(0, 0, destinationBitmap.Width, destinationBitmap.Height))
|
||||
{
|
||||
destinationBitmap.SetPixelData(sourceBitmap.GetPixelData());
|
||||
return;
|
||||
}
|
||||
|
||||
// The basic process is
|
||||
// 1. Copy from source bitmap region to a new PixelBitmapContent<Vector4> using sourceBitmap.TryCopyTo()
|
||||
// 2. If source and destination regions are a different size, resize Vector4 version
|
||||
// 3. Copy from Vector4 to destination region using destinationBitmap.TryCopyFrom()
|
||||
|
||||
// Copy from the source to the intermediate Vector4 format
|
||||
var intermediate = new PixelBitmapContent<Vector4>(sourceRegion.Width, sourceRegion.Height);
|
||||
var intermediateRegion = new Rectangle(0, 0, intermediate.Width, intermediate.Height);
|
||||
if (sourceBitmap.TryCopyTo(intermediate, sourceRegion, intermediateRegion))
|
||||
{
|
||||
// Resize the intermediate if required
|
||||
if (intermediate.Width != destinationRegion.Width || intermediate.Height != destinationRegion.Height)
|
||||
intermediate = intermediate.Resize(destinationRegion.Width, destinationRegion.Height) as PixelBitmapContent<Vector4>;
|
||||
// Copy from the intermediate to the destination
|
||||
if (destinationBitmap.TryCopyFrom(intermediate, new Rectangle(0, 0, intermediate.Width, intermediate.Height), destinationRegion))
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got here, one of the above steps didn't work
|
||||
throw new InvalidOperationException("Could not copy between " + sourceFormat + " and " + destinationFormat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads encoded bitmap content.
|
||||
/// </summary>
|
||||
/// <returns>Array containing encoded bitmap data.</returns>
|
||||
public abstract byte[] GetPixelData();
|
||||
|
||||
/// <summary>
|
||||
/// Writes encoded bitmap content.
|
||||
/// </summary>
|
||||
/// <param name="sourceData">Array containing encoded bitmap data to be set.</param>
|
||||
public abstract void SetPixelData(byte[] sourceData);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap resource.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}, {1}x{2}", GetType().Name, Width, Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to copy a region from a specified bitmap.
|
||||
/// </summary>
|
||||
/// <param name="sourceBitmap">BitmapContent being copied.</param>
|
||||
/// <param name="sourceRegion">Location of sourceBitmap.</param>
|
||||
/// <param name="destinationRegion">Region of destination bitmap to be overwritten.</param>
|
||||
/// <returns>true if region copy is supported; false otherwise.</returns>
|
||||
protected abstract bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to copy a region of the specified bitmap onto another.
|
||||
/// </summary>
|
||||
/// <param name="destinationBitmap">BitmapContent being overwritten.</param>
|
||||
/// <param name="sourceRegion">Location of the source bitmap.</param>
|
||||
/// <param name="destinationRegion">Region of destination bitmap to be overwritten.</param>
|
||||
/// <returns>true if region copy is supported; false otherwise.</returns>
|
||||
protected abstract bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public abstract bool TryGetFormat(out SurfaceFormat format);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the arguments to the Copy function.
|
||||
/// </summary>
|
||||
/// <param name="sourceBitmap">BitmapContent being copied.</param>
|
||||
/// <param name="sourceRegion">Location of sourceBitmap.</param>
|
||||
/// <param name="destinationBitmap">BitmapContent being overwritten.</param>
|
||||
/// <param name="destinationRegion">Region of bitmap to be overwritten.</param>
|
||||
protected static void ValidateCopyArguments(BitmapContent sourceBitmap, Rectangle sourceRegion, BitmapContent destinationBitmap, Rectangle destinationRegion)
|
||||
{
|
||||
if (sourceBitmap == null)
|
||||
throw new ArgumentNullException("sourceBitmap");
|
||||
if (destinationBitmap == null)
|
||||
throw new ArgumentNullException("destinationBitmap");
|
||||
// Make sure regions are within the bounds of the bitmaps
|
||||
if (sourceRegion.Left < 0
|
||||
|| sourceRegion.Top < 0
|
||||
|| sourceRegion.Width <= 0
|
||||
|| sourceRegion.Height <= 0
|
||||
|| sourceRegion.Right > sourceBitmap.Width
|
||||
|| sourceRegion.Bottom > sourceBitmap.Height)
|
||||
throw new ArgumentOutOfRangeException("sourceRegion");
|
||||
if (destinationRegion.Left < 0
|
||||
|| destinationRegion.Top < 0
|
||||
|| destinationRegion.Width <= 0
|
||||
|| destinationRegion.Height <= 0
|
||||
|| destinationRegion.Right > destinationBitmap.Width
|
||||
|| destinationRegion.Bottom > destinationBitmap.Height)
|
||||
throw new ArgumentOutOfRangeException("destinationRegion");
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an animation skeleton.
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerDisplay("Bone '{Name}'")]
|
||||
public class BoneContent : NodeContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BoneContent.
|
||||
/// </summary>
|
||||
public BoneContent()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties for managing a bone weight.
|
||||
/// </summary>
|
||||
public struct BoneWeight
|
||||
{
|
||||
string boneName;
|
||||
float weight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the bone.
|
||||
/// </summary>
|
||||
public string BoneName
|
||||
{
|
||||
get
|
||||
{
|
||||
return boneName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of bone influence, ranging from zero to one. The complete set of weights in a BoneWeightCollection should sum to one.
|
||||
/// </summary>
|
||||
public float Weight
|
||||
{
|
||||
get
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
weight = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BoneWeight with the specified name and weight.
|
||||
/// </summary>
|
||||
/// <param name="boneName">Name of the bone.</param>
|
||||
/// <param name="weight">Amount of influence, ranging from zero to one.</param>
|
||||
public BoneWeight(string boneName, float weight)
|
||||
{
|
||||
this.boneName = boneName;
|
||||
this.weight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of bone weights of a vertex.
|
||||
/// </summary>
|
||||
public sealed class BoneWeightCollection : Collection<BoneWeight>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of BoneWeightCollection.
|
||||
/// </summary>
|
||||
public BoneWeightCollection()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the contents of the weights list.
|
||||
/// </summary>
|
||||
public void NormalizeWeights()
|
||||
{
|
||||
// Normalization does the following:
|
||||
//
|
||||
// - Sorts weights such that the most significant weight is first.
|
||||
// - Removes zero-value entries.
|
||||
// - Adjusts values so the sum equals one.
|
||||
//
|
||||
// Throws InvalidContentException if all weights are zero.
|
||||
NormalizeWeights(int.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the contents of the bone weights list.
|
||||
/// </summary>
|
||||
/// <param name="maxWeights">Maximum number of weights allowed.</param>
|
||||
public void NormalizeWeights(int maxWeights)
|
||||
{
|
||||
// Normalization does the following:
|
||||
//
|
||||
// - Sorts weights such that the most significant weight is first.
|
||||
// - Removes zero-value entries.
|
||||
// - Discards weights with the smallest value until there are maxWeights or less in the list.
|
||||
// - Adjusts values so the sum equals one.
|
||||
//
|
||||
// Throws InvalidContentException if all weights are zero.
|
||||
|
||||
var weights = (List<BoneWeight>)Items;
|
||||
|
||||
// Sort into descending order
|
||||
weights.Sort((b1, b2) => b2.Weight.CompareTo(b1.Weight));
|
||||
|
||||
// Find the sum to validate we have weights and to normalize the weights
|
||||
float sum = 0.0f;
|
||||
int index = 0;
|
||||
// Cannot use a foreach or for because the index may not always increment and the length of the list may change.
|
||||
while (index < weights.Count)
|
||||
{
|
||||
float weight = weights[index].Weight;
|
||||
if ((weight > 0.0f) && (index < maxWeights))
|
||||
{
|
||||
sum += weight;
|
||||
++index;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discard any zero weights or if we have exceeded the maximum number of weights
|
||||
weights.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (sum == 0.0f)
|
||||
throw new InvalidContentException("Total bone weights in a collection must not be zero");
|
||||
|
||||
// Normalize each weight
|
||||
int count = weights.Count();
|
||||
// Old-school trick. Multiplication is faster than division, so multiply by the inverse.
|
||||
float invSum = 1.0f / sum;
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
BoneWeight bw = weights[index];
|
||||
bw.Weight *= invSum;
|
||||
weights[index] = bw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
// 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 Microsoft.Xna.Framework.Content.Pipeline.Processors;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
internal class DefaultTextureProfile : TextureProfile
|
||||
{
|
||||
public override bool Supports(TargetPlatform platform)
|
||||
{
|
||||
return platform == TargetPlatform.Android ||
|
||||
platform == TargetPlatform.DesktopGL ||
|
||||
platform == TargetPlatform.MacOSX ||
|
||||
platform == TargetPlatform.NativeClient ||
|
||||
platform == TargetPlatform.RaspberryPi ||
|
||||
platform == TargetPlatform.Windows ||
|
||||
platform == TargetPlatform.WindowsPhone8 ||
|
||||
platform == TargetPlatform.WindowsStoreApp ||
|
||||
platform == TargetPlatform.iOS;
|
||||
}
|
||||
|
||||
private static bool IsCompressedTextureFormat(TextureProcessorOutputFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case TextureProcessorOutputFormat.AtcCompressed:
|
||||
case TextureProcessorOutputFormat.DxtCompressed:
|
||||
case TextureProcessorOutputFormat.Etc1Compressed:
|
||||
case TextureProcessorOutputFormat.PvrCompressed:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static TextureProcessorOutputFormat GetTextureFormatForPlatform(TextureProcessorOutputFormat format, TargetPlatform platform)
|
||||
{
|
||||
// Select the default texture compression format for the target platform
|
||||
if (format == TextureProcessorOutputFormat.Compressed)
|
||||
{
|
||||
if (platform == TargetPlatform.iOS)
|
||||
format = TextureProcessorOutputFormat.PvrCompressed;
|
||||
else if (platform == TargetPlatform.Android)
|
||||
format = TextureProcessorOutputFormat.Etc1Compressed;
|
||||
else
|
||||
format = TextureProcessorOutputFormat.DxtCompressed;
|
||||
}
|
||||
|
||||
if (IsCompressedTextureFormat(format))
|
||||
{
|
||||
// Make sure the target platform supports the selected texture compression format
|
||||
if (platform == TargetPlatform.iOS)
|
||||
{
|
||||
if (format != TextureProcessorOutputFormat.PvrCompressed)
|
||||
throw new PlatformNotSupportedException("iOS platform only supports PVR texture compression");
|
||||
}
|
||||
else if (platform == TargetPlatform.Windows ||
|
||||
platform == TargetPlatform.WindowsPhone8 ||
|
||||
platform == TargetPlatform.WindowsStoreApp ||
|
||||
platform == TargetPlatform.DesktopGL ||
|
||||
platform == TargetPlatform.MacOSX ||
|
||||
platform == TargetPlatform.NativeClient)
|
||||
{
|
||||
if (format != TextureProcessorOutputFormat.DxtCompressed)
|
||||
throw new PlatformNotSupportedException(format + " platform only supports DXT texture compression");
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
public override void Requirements(ContentProcessorContext context, TextureProcessorOutputFormat format, out bool requiresPowerOfTwo, out bool requiresSquare)
|
||||
{
|
||||
if (format == TextureProcessorOutputFormat.Compressed)
|
||||
format = GetTextureFormatForPlatform(format, context.TargetPlatform);
|
||||
|
||||
// Does it require POT textures?
|
||||
switch (format)
|
||||
{
|
||||
default:
|
||||
requiresPowerOfTwo = false;
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.DxtCompressed:
|
||||
requiresPowerOfTwo = context.TargetProfile == GraphicsProfile.Reach;
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.PvrCompressed:
|
||||
case TextureProcessorOutputFormat.Etc1Compressed:
|
||||
requiresPowerOfTwo = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Does it require square textures?
|
||||
switch (format)
|
||||
{
|
||||
default:
|
||||
requiresSquare = false;
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.PvrCompressed:
|
||||
requiresSquare = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PlatformCompressTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont)
|
||||
{
|
||||
format = GetTextureFormatForPlatform(format, context.TargetPlatform);
|
||||
|
||||
// Make sure we're in a floating point format
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Vector4>));
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case TextureProcessorOutputFormat.AtcCompressed:
|
||||
GraphicsUtil.CompressAti(context, content, isSpriteFont);
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.Color16Bit:
|
||||
GraphicsUtil.CompressColor16Bit(context, content);
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.DxtCompressed:
|
||||
GraphicsUtil.CompressDxt(context, content, isSpriteFont);
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.Etc1Compressed:
|
||||
GraphicsUtil.CompressEtc1(context, content, isSpriteFont);
|
||||
break;
|
||||
|
||||
case TextureProcessorOutputFormat.PvrCompressed:
|
||||
GraphicsUtil.CompressPvrtc(context, content, isSpriteFont);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class DualTextureMaterialContent : MaterialContent
|
||||
{
|
||||
public const string AlphaKey = "Alpha";
|
||||
public const string DiffuseColorKey = "DiffuseColor";
|
||||
public const string TextureKey = "Texture";
|
||||
public const string Texture2Key = "Texture2";
|
||||
public const string VertexColorEnabledKey = "VertexColorEnabled";
|
||||
|
||||
public float? Alpha
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(AlphaKey); }
|
||||
set { SetProperty(AlphaKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? DiffuseColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(DiffuseColorKey); }
|
||||
set { SetProperty(DiffuseColorKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture
|
||||
{
|
||||
get { return GetTexture(TextureKey); }
|
||||
set { SetTexture(TextureKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture2
|
||||
{
|
||||
get { return GetTexture(Texture2Key); }
|
||||
set { SetTexture(Texture2Key, value); }
|
||||
}
|
||||
|
||||
public bool? VertexColorEnabled
|
||||
{
|
||||
get { return GetValueTypeProperty<bool>(VertexColorEnabledKey); }
|
||||
set { SetProperty(VertexColorEnabledKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class Dxt1BitmapContent : DxtBitmapContent
|
||||
{
|
||||
public Dxt1BitmapContent(int width, int height)
|
||||
: base(8, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.Dxt1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "DXT1 " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class Dxt3BitmapContent : DxtBitmapContent
|
||||
{
|
||||
public Dxt3BitmapContent(int width, int height)
|
||||
: base(16, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.Dxt3;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "DXT3 " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class Dxt5BitmapContent : DxtBitmapContent
|
||||
{
|
||||
public Dxt5BitmapContent(int width, int height)
|
||||
: base(16, width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.Dxt5;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "DXT5 " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
using Nvidia.TextureTools;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public abstract class DxtBitmapContent : BitmapContent
|
||||
{
|
||||
private byte[] _bitmapData;
|
||||
private int _blockSize;
|
||||
private SurfaceFormat _format;
|
||||
|
||||
private int _nvttWriteOffset;
|
||||
|
||||
protected DxtBitmapContent(int blockSize)
|
||||
{
|
||||
if (!((blockSize == 8) || (blockSize == 16)))
|
||||
throw new ArgumentException("Invalid block size");
|
||||
_blockSize = blockSize;
|
||||
TryGetFormat(out _format);
|
||||
}
|
||||
|
||||
protected DxtBitmapContent(int blockSize, int width, int height)
|
||||
: this(blockSize)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public override byte[] GetPixelData()
|
||||
{
|
||||
return _bitmapData;
|
||||
}
|
||||
|
||||
public override void SetPixelData(byte[] sourceData)
|
||||
{
|
||||
_bitmapData = sourceData;
|
||||
}
|
||||
|
||||
private void NvttBeginImage(int size, int width, int height, int depth, int face, int miplevel)
|
||||
{
|
||||
_bitmapData = new byte[size];
|
||||
_nvttWriteOffset = 0;
|
||||
}
|
||||
|
||||
private bool NvttWriteImage(IntPtr data, int length)
|
||||
{
|
||||
Marshal.Copy(data, _bitmapData, _nvttWriteOffset, length);
|
||||
_nvttWriteOffset += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void NvttEndImage()
|
||||
{
|
||||
}
|
||||
|
||||
private static void PrepareNVTT(byte[] data)
|
||||
{
|
||||
for (var x = 0; x < data.Length; x += 4)
|
||||
{
|
||||
// NVTT wants BGRA where our source is RGBA so
|
||||
// we swap the red and blue channels.
|
||||
data[x] ^= data[x + 2];
|
||||
data[x + 2] ^= data[x];
|
||||
data[x] ^= data[x + 2];
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareNVTT_DXT1(byte[] data, out bool hasTransparency)
|
||||
{
|
||||
hasTransparency = false;
|
||||
|
||||
for (var x = 0; x < data.Length; x += 4)
|
||||
{
|
||||
// NVTT wants BGRA where our source is RGBA so
|
||||
// we swap the red and blue channels.
|
||||
data[x] ^= data[x + 2];
|
||||
data[x + 2] ^= data[x];
|
||||
data[x] ^= data[x + 2];
|
||||
|
||||
// Look for non-opaque pixels.
|
||||
var alpha = data[x + 3];
|
||||
if (alpha < 255)
|
||||
hasTransparency = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
SetPixelData(sourceBitmap.GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Add a XNA unit test to see what it does
|
||||
// my guess is that this is invalid for DXT.
|
||||
//
|
||||
// Destination region copy is not yet supported
|
||||
if (destinationRegion != new Rectangle(0, 0, Width, Height))
|
||||
return false;
|
||||
|
||||
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// NVTT wants 8bit data in BGRA format.
|
||||
var colorBitmap = new PixelBitmapContent<Color>(sourceBitmap.Width, sourceBitmap.Height);
|
||||
BitmapContent.Copy(sourceBitmap, colorBitmap);
|
||||
var sourceData = colorBitmap.GetPixelData();
|
||||
var dataHandle = GCHandle.Alloc(sourceData, GCHandleType.Pinned);
|
||||
|
||||
AlphaMode alphaMode;
|
||||
Format outputFormat;
|
||||
var alphaDither = false;
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat.Dxt1:
|
||||
case SurfaceFormat.Dxt1SRgb:
|
||||
{
|
||||
bool hasTransparency;
|
||||
PrepareNVTT_DXT1(sourceData, out hasTransparency);
|
||||
outputFormat = hasTransparency ? Format.DXT1a : Format.DXT1;
|
||||
alphaMode = hasTransparency ? AlphaMode.Transparency : AlphaMode.None;
|
||||
alphaDither = true;
|
||||
break;
|
||||
}
|
||||
case SurfaceFormat.Dxt3:
|
||||
case SurfaceFormat.Dxt3SRgb:
|
||||
{
|
||||
PrepareNVTT(sourceData);
|
||||
outputFormat = Format.DXT3;
|
||||
alphaMode = AlphaMode.Transparency;
|
||||
break;
|
||||
}
|
||||
case SurfaceFormat.Dxt5:
|
||||
case SurfaceFormat.Dxt5SRgb:
|
||||
{
|
||||
PrepareNVTT(sourceData);
|
||||
outputFormat = Format.DXT5;
|
||||
alphaMode = AlphaMode.Transparency;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid DXT surface format!");
|
||||
}
|
||||
|
||||
// Do all the calls to the NVTT wrapper within this handler
|
||||
// so we properly clean up if things blow up.
|
||||
try
|
||||
{
|
||||
var dataPtr = dataHandle.AddrOfPinnedObject();
|
||||
|
||||
var inputOptions = new InputOptions();
|
||||
inputOptions.SetTextureLayout(TextureType.Texture2D, colorBitmap.Width, colorBitmap.Height, 1);
|
||||
inputOptions.SetMipmapData(dataPtr, colorBitmap.Width, colorBitmap.Height, 1, 0, 0);
|
||||
inputOptions.SetMipmapGeneration(false);
|
||||
inputOptions.SetGamma(1.0f, 1.0f);
|
||||
inputOptions.SetAlphaMode(alphaMode);
|
||||
|
||||
var compressionOptions = new CompressionOptions();
|
||||
compressionOptions.SetFormat(outputFormat);
|
||||
compressionOptions.SetQuality(Quality.Normal);
|
||||
|
||||
// TODO: This isn't working which keeps us from getting the
|
||||
// same alpha dither behavior on DXT1 as XNA.
|
||||
//
|
||||
// See https://github.com/MonoGame/MonoGame/issues/6259
|
||||
//
|
||||
//if (alphaDither)
|
||||
//compressionOptions.SetQuantization(false, false, true);
|
||||
|
||||
var outputOptions = new OutputOptions();
|
||||
outputOptions.SetOutputHeader(false);
|
||||
outputOptions.SetOutputOptionsOutputHandler(NvttBeginImage, NvttWriteImage, NvttEndImage);
|
||||
|
||||
var dxtCompressor = new Compressor();
|
||||
dxtCompressor.Compress(inputOptions, compressionOptions, outputOptions);
|
||||
}
|
||||
finally
|
||||
{
|
||||
dataHandle.Free();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
var fullRegion = new Rectangle(0, 0, Width, Height);
|
||||
if ((format == destinationFormat) && (sourceRegion == fullRegion) && (sourceRegion == destinationRegion))
|
||||
{
|
||||
destinationBitmap.SetPixelData(GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other support for copying from a DXT texture yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the source code for a DirectX Effect, loaded from a .fx file.
|
||||
/// </summary>
|
||||
public class EffectContent : ContentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of EffectContent.
|
||||
/// </summary>
|
||||
public EffectContent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the effect program source code.
|
||||
/// </summary>
|
||||
public string EffectCode { get; set; }
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// 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 Microsoft.Xna.Framework.Content.Pipeline.Processors;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class EffectMaterialContent : MaterialContent
|
||||
{
|
||||
public const string EffectKey = "Effect";
|
||||
public const string CompiledEffectKey = "CompiledEffect";
|
||||
|
||||
public ExternalReference<EffectContent> Effect
|
||||
{
|
||||
get { return GetReferenceTypeProperty<ExternalReference<EffectContent>>(EffectKey); }
|
||||
set { SetProperty(EffectKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<CompiledEffectContent> CompiledEffect
|
||||
{
|
||||
get { return GetReferenceTypeProperty<ExternalReference<CompiledEffectContent>>(CompiledEffectKey); }
|
||||
set { SetProperty(CompiledEffectKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class EnvironmentMapMaterialContent : MaterialContent
|
||||
{
|
||||
public const string AlphaKey = "Alpha";
|
||||
public const string DiffuseColorKey = "DiffuseColor";
|
||||
public const string EmissiveColorKey = "EmissiveColor";
|
||||
public const string EnvironmentMapKey = "EnvironmentMap";
|
||||
public const string EnvironmentMapAmountKey = "EnvironmentMapAmount";
|
||||
public const string EnvironmentMapSpecularKey = " EnvironmentMapSpecular";
|
||||
public const string FresnelFactorKey = "FresnelFactor";
|
||||
public const string TextureKey = "Texture";
|
||||
|
||||
public float? Alpha
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(AlphaKey); }
|
||||
set { SetProperty(AlphaKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? DiffuseColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(DiffuseColorKey); }
|
||||
set { SetProperty(DiffuseColorKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? EmissiveColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(EmissiveColorKey); }
|
||||
set { SetProperty(EmissiveColorKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> EnvironmentMap
|
||||
{
|
||||
get { return GetTexture(EnvironmentMapKey); }
|
||||
set { SetTexture(EnvironmentMapKey, value); }
|
||||
}
|
||||
|
||||
public float? EnvironmentMapAmount
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(EnvironmentMapAmountKey); }
|
||||
set { SetProperty(EnvironmentMapAmountKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? EnvironmentMapSpecular
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(EnvironmentMapSpecularKey); }
|
||||
set { SetProperty(EnvironmentMapSpecularKey, value); }
|
||||
}
|
||||
|
||||
public float? FresnelFactor
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(FresnelFactorKey); }
|
||||
set { SetProperty(FresnelFactorKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture
|
||||
{
|
||||
get { return GetTexture(TextureKey); }
|
||||
set { SetTexture(TextureKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
using PVRTexLibNET;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports the processing of a texture compressed using ETC1.
|
||||
/// </summary>
|
||||
public class Etc1BitmapContent : BitmapContent
|
||||
{
|
||||
byte[] _data;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of Etc1BitmapContent.
|
||||
/// </summary>
|
||||
protected Etc1BitmapContent()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of Etc1BitmapContent with the specified width or height.
|
||||
/// </summary>
|
||||
/// <param name="width">Width in pixels of the bitmap resource.</param>
|
||||
/// <param name="height">Height in pixels of the bitmap resource.</param>
|
||||
public Etc1BitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
public override byte[] GetPixelData()
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
public override void SetPixelData(byte[] sourceData)
|
||||
{
|
||||
int bytesRequired = ((Width + 3) >> 2) * ((Height + 3) >> 2) * SurfaceFormat.RgbEtc1.GetSize();
|
||||
if (bytesRequired != sourceData.Length)
|
||||
throw new ArgumentException("ETC1 bitmap with width " + Width + " and height " + Height + " needs "
|
||||
+ bytesRequired + " bytes. Received " + sourceData.Length + " bytes");
|
||||
|
||||
if (_data == null || _data.Length != bytesRequired)
|
||||
_data = new byte[bytesRequired];
|
||||
Buffer.BlockCopy(sourceData, 0, _data, 0, bytesRequired);
|
||||
}
|
||||
|
||||
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
return false;
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (SurfaceFormat.RgbEtc1 == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
SetPixelData(sourceBitmap.GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Destination region copy is not yet supported
|
||||
if (destinationRegion != new Rectangle(0, 0, Width, Height))
|
||||
return false;
|
||||
|
||||
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the texture object in the PVR library
|
||||
var sourceData = sourceBitmap.GetPixelData();
|
||||
var rgba32F = (PixelFormat)0x2020202061626772; // static const PixelType PVRStandard32PixelType = PixelType('r', 'g', 'b', 'a', 32, 32, 32, 32);
|
||||
using (var pvrTexture = PVRTexture.CreateTexture(sourceData, (uint)sourceBitmap.Width, (uint)sourceBitmap.Height, 1,
|
||||
rgba32F, true, VariableType.Float, ColourSpace.lRGB))
|
||||
{
|
||||
// Resize the bitmap if needed
|
||||
if ((sourceBitmap.Width != Width) || (sourceBitmap.Height != Height))
|
||||
pvrTexture.Resize((uint)Width, (uint)Height, 1, ResizeMode.Cubic);
|
||||
pvrTexture.Transcode(PixelFormat.ETC1, VariableType.UnsignedByte, ColourSpace.lRGB /*, CompressorQuality.ETCMediumPerceptual, true*/);
|
||||
var texDataSize = pvrTexture.GetTextureDataSize(0);
|
||||
var texData = new byte[texDataSize];
|
||||
pvrTexture.GetTextureData(texData, texDataSize);
|
||||
SetPixelData(texData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
return false;
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (SurfaceFormat.RgbEtc1 == destinationFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
destinationBitmap.SetPixelData(GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other support for copying from a ETC1 texture yet
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbEtc1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "ETC1 " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// 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 Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Assorted helpers for doing useful things with bitmaps.
|
||||
internal static class BitmapUtils
|
||||
{
|
||||
// Checks whether an area of a bitmap contains entirely the specified alpha value.
|
||||
public static bool IsAlphaEntirely(byte expectedAlpha, BitmapContent bitmap, Rectangle? region = null)
|
||||
{
|
||||
var bitmapRegion = region.HasValue ? region.Value : new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||
// Works with PixelBitmapContent<byte> at this stage
|
||||
if (bitmap is PixelBitmapContent<byte>)
|
||||
{
|
||||
var bmp = bitmap as PixelBitmapContent<byte>;
|
||||
for (int y = 0; y < bitmapRegion.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bitmapRegion.Width; x++)
|
||||
{
|
||||
var alpha = bmp.GetPixel(bitmapRegion.X + x, bitmapRegion.Y + y);
|
||||
if (alpha != expectedAlpha)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (bitmap is PixelBitmapContent<Color>)
|
||||
{
|
||||
var bmp = bitmap as PixelBitmapContent<Color>;
|
||||
for (int y = 0; y < bitmapRegion.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bitmapRegion.Width; x++)
|
||||
{
|
||||
var alpha = bmp.GetPixel(bitmapRegion.X + x, bitmapRegion.Y + y).A;
|
||||
if (alpha != expectedAlpha)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
throw new ArgumentException("Expected PixelBitmapContent<byte> or PixelBitmapContent<Color>, got " + bitmap.GetType().Name, "bitmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Describes a range of consecutive characters that should be included in the font.
|
||||
[TypeConverter(typeof(CharacterRegionTypeConverter))]
|
||||
public struct CharacterRegion
|
||||
{
|
||||
public char Start;
|
||||
public char End;
|
||||
|
||||
// Enumerates all characters within the region.
|
||||
public IEnumerable<Char> Characters()
|
||||
{
|
||||
for (var c = Start; c <= End; c++)
|
||||
{
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor.
|
||||
public CharacterRegion(char start, char end)
|
||||
{
|
||||
if (start > end)
|
||||
throw new ArgumentException();
|
||||
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
// Default to just the base ASCII character set.
|
||||
public static CharacterRegion Default = new CharacterRegion(' ', '~');
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test if there is an element in this enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the element</typeparam>
|
||||
/// <param name="source">The enumerable source.</param>
|
||||
/// <returns><c>true</c> if there is an element in this enumeration, <c>false</c> otherwise</returns>
|
||||
public static bool Any<T>(IEnumerable<T> source)
|
||||
{
|
||||
return source.GetEnumerator().MoveNext();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Select elements from an enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the T source.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the T result.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="selector">The selector.</param>
|
||||
/// <returns>A enumeration of selected values</returns>
|
||||
public static IEnumerable<TResult> SelectMany<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
|
||||
{
|
||||
foreach (TSource sourceItem in source)
|
||||
{
|
||||
foreach (TResult result in selector(sourceItem))
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects distinct elements from an enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the T source.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="comparer">The comparer.</param>
|
||||
/// <returns>A enumeration of selected values</returns>
|
||||
public static IEnumerable<TSource> Distinct<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer = null)
|
||||
{
|
||||
if (comparer == null)
|
||||
comparer = EqualityComparer<TSource>.Default;
|
||||
|
||||
// using Dictionary is not really efficient but easy to implement
|
||||
var values = new Dictionary<TSource, object>(comparer);
|
||||
foreach (TSource sourceItem in source)
|
||||
{
|
||||
if (!values.ContainsKey(sourceItem))
|
||||
{
|
||||
values.Add(sourceItem, null);
|
||||
yield return sourceItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class CharacterRegionTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
// Input must be a string.
|
||||
string source = value as string;
|
||||
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
// Supported input formats:
|
||||
// A
|
||||
// A-Z
|
||||
// 32-127
|
||||
// 0x20-0x7F
|
||||
|
||||
var splitStr = source.Split('-');
|
||||
var split = new char[splitStr.Length];
|
||||
for (int i = 0; i < splitStr.Length; i++)
|
||||
{
|
||||
split[i] = ConvertCharacter(splitStr[i]);
|
||||
}
|
||||
|
||||
switch (split.Length)
|
||||
{
|
||||
case 1:
|
||||
// Only a single character (eg. "a").
|
||||
return new CharacterRegion(split[0], split[0]);
|
||||
|
||||
case 2:
|
||||
// Range of characters (eg. "a-z").
|
||||
return new CharacterRegion(split[0], split[1]);
|
||||
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char ConvertCharacter(string value)
|
||||
{
|
||||
if (value.Length == 1)
|
||||
{
|
||||
// Single character directly specifies a codepoint.
|
||||
return value[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise it must be an integer (eg. "32" or "0x20").
|
||||
return (char)(int)intConverter.ConvertFromInvariantString(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ABCFloat
|
||||
{
|
||||
public float A;
|
||||
public float B;
|
||||
public float C;
|
||||
}
|
||||
|
||||
// Represents a single character within a font.
|
||||
internal class Glyph
|
||||
{
|
||||
// Constructor.
|
||||
public Glyph(char character, BitmapContent bitmap, Rectangle? subrect = null)
|
||||
{
|
||||
this.Character = character;
|
||||
this.Bitmap = bitmap;
|
||||
this.Subrect = subrect.GetValueOrDefault(new Rectangle(0, 0, bitmap.Width, bitmap.Height));
|
||||
this.Width = bitmap.Width;
|
||||
this.Height = bitmap.Height;
|
||||
}
|
||||
|
||||
// Unicode codepoint.
|
||||
public char Character;
|
||||
|
||||
// Glyph image data (may only use a portion of a larger bitmap).
|
||||
public BitmapContent Bitmap;
|
||||
public Rectangle Subrect;
|
||||
|
||||
// Layout information.
|
||||
public float XOffset;
|
||||
public float YOffset;
|
||||
public int Width;
|
||||
public int Height;
|
||||
|
||||
public float XAdvance;
|
||||
|
||||
public ABCFloat CharacterWidths;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// 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 Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Crops unused space from around the edge of a glyph bitmap.
|
||||
internal static class GlyphCropper
|
||||
{
|
||||
public static void Crop(Glyph glyph)
|
||||
{
|
||||
// Crop the top.
|
||||
while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, glyph.Subrect.Width, 1)))
|
||||
{
|
||||
glyph.Subrect.Y++;
|
||||
glyph.Subrect.Height--;
|
||||
|
||||
glyph.YOffset++;
|
||||
}
|
||||
|
||||
// Crop the bottom.
|
||||
while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Bottom - 1, glyph.Subrect.Width, 1)))
|
||||
{
|
||||
glyph.Subrect.Height--;
|
||||
}
|
||||
|
||||
// Crop the left.
|
||||
while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, 1, glyph.Subrect.Height)))
|
||||
{
|
||||
glyph.Subrect.X++;
|
||||
glyph.Subrect.Width--;
|
||||
|
||||
glyph.XOffset++;
|
||||
}
|
||||
|
||||
// Crop the right.
|
||||
while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.Right - 1, glyph.Subrect.Y, 1, glyph.Subrect.Height)))
|
||||
{
|
||||
glyph.Subrect.Width--;
|
||||
|
||||
glyph.XAdvance++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Helper for arranging many small bitmaps onto a single larger surface.
|
||||
internal static class GlyphPacker
|
||||
{
|
||||
public static BitmapContent ArrangeGlyphs(Glyph[] sourceGlyphs, bool requirePOT, bool requireSquare)
|
||||
{
|
||||
// Build up a list of all the glyphs needing to be arranged.
|
||||
var glyphs = new List<ArrangedGlyph>();
|
||||
|
||||
for (int i = 0; i < sourceGlyphs.Length; i++)
|
||||
{
|
||||
var glyph = new ArrangedGlyph();
|
||||
|
||||
glyph.Source = sourceGlyphs[i];
|
||||
|
||||
// Leave a one pixel border around every glyph in the output bitmap.
|
||||
glyph.Width = sourceGlyphs[i].Subrect.Width + 2;
|
||||
glyph.Height = sourceGlyphs[i].Subrect.Height + 2;
|
||||
|
||||
glyphs.Add(glyph);
|
||||
}
|
||||
|
||||
// Sort so the largest glyphs get arranged first.
|
||||
glyphs.Sort(CompareGlyphSizes);
|
||||
|
||||
// Work out how big the output bitmap should be.
|
||||
int outputWidth = GuessOutputWidth(sourceGlyphs);
|
||||
int outputHeight = 0;
|
||||
|
||||
// Choose positions for each glyph, one at a time.
|
||||
for (int i = 0; i < glyphs.Count; i++)
|
||||
{
|
||||
PositionGlyph(glyphs, i, outputWidth);
|
||||
|
||||
outputHeight = Math.Max(outputHeight, glyphs[i].Y + glyphs[i].Height);
|
||||
}
|
||||
|
||||
// Create the merged output bitmap.
|
||||
outputHeight = MakeValidTextureSize(outputHeight, requirePOT);
|
||||
|
||||
if (requireSquare)
|
||||
{
|
||||
outputHeight = Math.Max (outputWidth, outputHeight);
|
||||
outputWidth = outputHeight;
|
||||
}
|
||||
|
||||
return CopyGlyphsToOutput(glyphs, outputWidth, outputHeight);
|
||||
}
|
||||
|
||||
// Once arranging is complete, copies each glyph to its chosen position in the single larger output bitmap.
|
||||
static BitmapContent CopyGlyphsToOutput(List<ArrangedGlyph> glyphs, int width, int height)
|
||||
{
|
||||
var output = new PixelBitmapContent<Color>(width, height);
|
||||
|
||||
foreach (var glyph in glyphs)
|
||||
{
|
||||
var sourceGlyph = glyph.Source;
|
||||
var sourceRegion = sourceGlyph.Subrect;
|
||||
var destinationRegion = new Rectangle(glyph.X + 1, glyph.Y + 1, sourceRegion.Width, sourceRegion.Height);
|
||||
|
||||
BitmapContent.Copy(sourceGlyph.Bitmap, sourceRegion, output, destinationRegion);
|
||||
|
||||
sourceGlyph.Bitmap = output;
|
||||
sourceGlyph.Subrect = destinationRegion;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Internal helper class keeps track of a glyph while it is being arranged.
|
||||
class ArrangedGlyph
|
||||
{
|
||||
public Glyph Source;
|
||||
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public int Width;
|
||||
public int Height;
|
||||
}
|
||||
|
||||
|
||||
// Works out where to position a single glyph.
|
||||
static void PositionGlyph(List<ArrangedGlyph> glyphs, int index, int outputWidth)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Is this position free for us to use?
|
||||
int intersects = FindIntersectingGlyph(glyphs, index, x, y);
|
||||
|
||||
if (intersects < 0)
|
||||
{
|
||||
glyphs[index].X = x;
|
||||
glyphs[index].Y = y;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip past the existing glyph that we collided with.
|
||||
x = glyphs[intersects].X + glyphs[intersects].Width;
|
||||
|
||||
// If we ran out of room to move to the right, try the next line down instead.
|
||||
if (x + glyphs[index].Width > outputWidth)
|
||||
{
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checks if a proposed glyph position collides with anything that we already arranged.
|
||||
static int FindIntersectingGlyph(List<ArrangedGlyph> glyphs, int index, int x, int y)
|
||||
{
|
||||
int w = glyphs[index].Width;
|
||||
int h = glyphs[index].Height;
|
||||
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
if (glyphs[i].X >= x + w)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].X + glyphs[i].Width <= x)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].Y >= y + h)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].Y + glyphs[i].Height <= y)
|
||||
continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Comparison function for sorting glyphs by size.
|
||||
static int CompareGlyphSizes(ArrangedGlyph a, ArrangedGlyph b)
|
||||
{
|
||||
const int heightWeight = 1024;
|
||||
|
||||
int aSize = a.Height * heightWeight + a.Width;
|
||||
int bSize = b.Height * heightWeight + b.Width;
|
||||
|
||||
if (aSize != bSize)
|
||||
return bSize.CompareTo(aSize);
|
||||
else
|
||||
return a.Source.Character.CompareTo(b.Source.Character);
|
||||
}
|
||||
|
||||
|
||||
// Heuristic guesses what might be a good output width for a list of glyphs.
|
||||
static int GuessOutputWidth(Glyph[] sourceGlyphs)
|
||||
{
|
||||
int maxWidth = 0;
|
||||
int totalSize = 0;
|
||||
|
||||
foreach (var glyph in sourceGlyphs)
|
||||
{
|
||||
maxWidth = Math.Max(maxWidth, glyph.Bitmap.Width);
|
||||
totalSize += glyph.Bitmap.Width * glyph.Bitmap.Height;
|
||||
}
|
||||
|
||||
int width = Math.Max((int)Math.Sqrt(totalSize), maxWidth);
|
||||
|
||||
return MakeValidTextureSize(width, true);
|
||||
}
|
||||
|
||||
|
||||
// Rounds a value up to the next larger valid texture size.
|
||||
static int MakeValidTextureSize(int value, bool requirePowerOfTwo)
|
||||
{
|
||||
// In case we want to compress the texture, make sure the size is a multiple of 4.
|
||||
const int blockSize = 4;
|
||||
|
||||
if (requirePowerOfTwo)
|
||||
{
|
||||
// Round up to a power of two.
|
||||
int powerOfTwo = blockSize;
|
||||
|
||||
while (powerOfTwo < value)
|
||||
powerOfTwo <<= 1;
|
||||
|
||||
return powerOfTwo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Round up to the specified block size.
|
||||
return (value + blockSize - 1) & ~(blockSize - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Importer interface allows the conversion tool to support multiple source font formats.
|
||||
internal interface IFontImporter
|
||||
{
|
||||
void Import(FontDescription options, string fontName);
|
||||
|
||||
IEnumerable<Glyph> Glyphs { get; }
|
||||
|
||||
float LineSpacing { get; }
|
||||
|
||||
int YOffsetMin { get; }
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using SharpFont;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Uses FreeType to rasterize TrueType fonts into a series of glyph bitmaps.
|
||||
internal class SharpFontImporter : IFontImporter
|
||||
{
|
||||
// Properties hold the imported font data.
|
||||
public IEnumerable<Glyph> Glyphs { get; private set; }
|
||||
|
||||
public float LineSpacing { get; private set; }
|
||||
|
||||
public int YOffsetMin { get; private set; }
|
||||
|
||||
// Size of the temp surface used for GDI+ rasterization.
|
||||
const int MaxGlyphSize = 1024;
|
||||
|
||||
Library lib = null;
|
||||
|
||||
public void Import(FontDescription options, string fontName)
|
||||
{
|
||||
lib = new Library();
|
||||
// Create a bunch of GDI+ objects.
|
||||
var face = CreateFontFace(options, fontName);
|
||||
try
|
||||
{
|
||||
// Which characters do we want to include?
|
||||
var characters = options.Characters;
|
||||
|
||||
var glyphList = new List<Glyph>();
|
||||
// Rasterize each character in turn.
|
||||
foreach (char character in characters)
|
||||
{
|
||||
var glyph = ImportGlyph(character, face);
|
||||
glyphList.Add(glyph);
|
||||
}
|
||||
Glyphs = glyphList;
|
||||
|
||||
// Store the font height.
|
||||
LineSpacing = face.Size.Metrics.Height >> 6;
|
||||
|
||||
// The height used to calculate the Y offset for each character.
|
||||
YOffsetMin = -face.Size.Metrics.Ascender >> 6;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (face != null)
|
||||
face.Dispose();
|
||||
if (lib != null)
|
||||
{
|
||||
lib.Dispose();
|
||||
lib = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Attempts to instantiate the requested GDI+ font object.
|
||||
private Face CreateFontFace(FontDescription options, string fontName)
|
||||
{
|
||||
try
|
||||
{
|
||||
const uint dpi = 96;
|
||||
var face = lib.NewFace(fontName, 0);
|
||||
var fixedSize = ((int)options.Size) << 6;
|
||||
face.SetCharSize(0, fixedSize, dpi, dpi);
|
||||
|
||||
if (face.FamilyName == "Microsoft Sans Serif" && options.FontName != "Microsoft Sans Serif")
|
||||
throw new PipelineException(string.Format("Font {0} is not installed on this computer.", options.FontName));
|
||||
|
||||
return face;
|
||||
|
||||
// A font substitution must have occurred.
|
||||
//throw new Exception(string.Format("Can't find font '{0}'.", options.FontName));
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Rasterizes a single character glyph.
|
||||
private Glyph ImportGlyph(char character, Face face)
|
||||
{
|
||||
uint glyphIndex = face.GetCharIndex(character);
|
||||
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
|
||||
// Render the character.
|
||||
BitmapContent glyphBitmap = null;
|
||||
if (face.Glyph.Bitmap.Width > 0 && face.Glyph.Bitmap.Rows > 0)
|
||||
{
|
||||
glyphBitmap = new PixelBitmapContent<byte>(face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows);
|
||||
byte[] gpixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows];
|
||||
//if the character bitmap has 1bpp we have to expand the buffer data to get the 8bpp pixel data
|
||||
//each byte in bitmap.bufferdata contains the value of to 8 pixels in the row
|
||||
//if bitmap is of width 10, each row has 2 bytes with 10 valid bits, and the last 6 bits of 2nd byte must be discarded
|
||||
if(face.Glyph.Bitmap.PixelMode == PixelMode.Mono)
|
||||
{
|
||||
//variables needed for the expansion, amount of written data, length of the data to write
|
||||
int written = 0, length = face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows;
|
||||
for(int i = 0; written < length; i++)
|
||||
{
|
||||
//width in pixels of each row
|
||||
int width = face.Glyph.Bitmap.Width;
|
||||
while(width > 0)
|
||||
{
|
||||
//valid data in the current byte
|
||||
int stride = MathHelper.Min(8, width);
|
||||
//copy the valid bytes to pixeldata
|
||||
//System.Array.Copy(ExpandByte(face.Glyph.Bitmap.BufferData[i]), 0, gpixelAlphas, written, stride);
|
||||
ExpandByteAndCopy(face.Glyph.Bitmap.BufferData[i], stride, gpixelAlphas, written);
|
||||
written += stride;
|
||||
width -= stride;
|
||||
if(width > 0)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Marshal.Copy(face.Glyph.Bitmap.Buffer, gpixelAlphas, 0, gpixelAlphas.Length);
|
||||
glyphBitmap.SetPixelData(gpixelAlphas);
|
||||
}
|
||||
|
||||
if (glyphBitmap == null)
|
||||
{
|
||||
var gHA = face.Glyph.Metrics.HorizontalAdvance >> 6;
|
||||
var gVA = face.Size.Metrics.Height >> 6;
|
||||
|
||||
gHA = gHA > 0 ? gHA : gVA;
|
||||
gVA = gVA > 0 ? gVA : gHA;
|
||||
|
||||
glyphBitmap = new PixelBitmapContent<byte>(gHA, gVA);
|
||||
}
|
||||
|
||||
// not sure about this at all
|
||||
var abc = new ABCFloat ();
|
||||
abc.A = face.Glyph.Metrics.HorizontalBearingX >> 6;
|
||||
abc.B = face.Glyph.Metrics.Width >> 6;
|
||||
abc.C = (face.Glyph.Metrics.HorizontalAdvance >> 6) - (abc.A + abc.B);
|
||||
|
||||
// Construct the output Glyph object.
|
||||
return new Glyph(character, glyphBitmap)
|
||||
{
|
||||
XOffset = -(face.Glyph.Advance.X >> 6),
|
||||
XAdvance = face.Glyph.Metrics.HorizontalAdvance >> 6,
|
||||
YOffset = -(face.Glyph.Metrics.HorizontalBearingY >> 6),
|
||||
CharacterWidths = abc
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads each individual bit of a byte from left to right and expands it to a full byte,
|
||||
/// ones get byte.maxvalue, and zeros get byte.minvalue.
|
||||
/// </summary>
|
||||
/// <param name="origin">Byte to expand and copy</param>
|
||||
/// <param name="length">Number of Bits of the Byte to copy, from 1 to 8</param>
|
||||
/// <param name="destination">Byte array where to copy the results</param>
|
||||
/// <param name="startIndex">Position where to begin copying the results in destination</param>
|
||||
private static void ExpandByteAndCopy(byte origin, int length, byte[] destination, int startIndex)
|
||||
{
|
||||
byte tmp;
|
||||
for(int i = 7; i > 7 - length; i--)
|
||||
{
|
||||
tmp = (byte) (1 << i);
|
||||
if(origin / tmp == 1)
|
||||
{
|
||||
destination[startIndex + 7 - i] = byte.MaxValue;
|
||||
origin -= tmp;
|
||||
}
|
||||
else
|
||||
destination[startIndex + 7 - i] = byte.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
internal class CharacterCollection : ICollection<char>
|
||||
{
|
||||
private List<char> _items;
|
||||
|
||||
public CharacterCollection()
|
||||
{
|
||||
_items = new List<char>();
|
||||
}
|
||||
|
||||
public CharacterCollection(IEnumerable<char> characters)
|
||||
{
|
||||
_items = new List<char>();
|
||||
foreach (var c in characters)
|
||||
Add(c);
|
||||
}
|
||||
|
||||
#region ICollection<char> Members
|
||||
|
||||
public void Add(char item)
|
||||
{
|
||||
if (!_items.Contains(item))
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(char item)
|
||||
{
|
||||
return _items.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(char[] array, int arrayIndex)
|
||||
{
|
||||
_items.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _items.Count; }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool Remove(char item)
|
||||
{
|
||||
return _items.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<char> Members
|
||||
|
||||
public IEnumerator<char> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides information to the FontDescriptionProcessor describing which font to rasterize, which font size to utilize, and which Unicode characters to include in the processor output.
|
||||
/// </summary>
|
||||
public class FontDescription : ContentItem
|
||||
{
|
||||
private char? defaultCharacter;
|
||||
private string fontName;
|
||||
private float size;
|
||||
private float spacing;
|
||||
private FontDescriptionStyle style;
|
||||
private bool useKerning;
|
||||
private CharacterCollection characters = new CharacterCollection();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the font, such as "Times New Roman" or "Arial". This value cannot be null or empty.
|
||||
/// </summary>
|
||||
[ContentSerializer(AllowNull = false)]
|
||||
public string FontName
|
||||
{
|
||||
get
|
||||
{
|
||||
return fontName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentNullException("FontName is null or an empty string.");
|
||||
fontName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size, in points, of the font.
|
||||
/// </summary>
|
||||
public float Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return size;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value <= 0.0f)
|
||||
throw new ArgumentOutOfRangeException("Size must be greater than zero.");
|
||||
size = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of space, in pixels, to insert between letters in a string.
|
||||
/// </summary>
|
||||
[ContentSerializer(Optional = true)]
|
||||
public float Spacing
|
||||
{
|
||||
get
|
||||
{
|
||||
return spacing;
|
||||
}
|
||||
set
|
||||
{
|
||||
spacing = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if kerning information is used when drawing characters.
|
||||
/// </summary>
|
||||
[ContentSerializer(Optional = true)]
|
||||
public bool UseKerning
|
||||
{
|
||||
get
|
||||
{
|
||||
return useKerning;
|
||||
}
|
||||
set
|
||||
{
|
||||
useKerning = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style of the font, expressed as a combination of one or more FontDescriptionStyle flags.
|
||||
/// </summary>
|
||||
public FontDescriptionStyle Style
|
||||
{
|
||||
get
|
||||
{
|
||||
return style;
|
||||
}
|
||||
set
|
||||
{
|
||||
style = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default character for the font.
|
||||
/// </summary>
|
||||
[ContentSerializer(Optional = true)]
|
||||
public Nullable<char> DefaultCharacter
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultCharacter;
|
||||
}
|
||||
set
|
||||
{
|
||||
defaultCharacter = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ContentSerializer(CollectionItemName = "CharacterRegion")]
|
||||
internal CharacterRegion[] CharacterRegions
|
||||
{
|
||||
get
|
||||
{
|
||||
var regions = new List<CharacterRegion>();
|
||||
var chars = Characters.ToList();
|
||||
chars.Sort();
|
||||
|
||||
var start = chars[0];
|
||||
var end = chars[0];
|
||||
|
||||
for (var i=1; i < chars.Count; i++)
|
||||
{
|
||||
if (chars[i] != (end+1))
|
||||
{
|
||||
regions.Add(new CharacterRegion(start, end));
|
||||
start = chars[i];
|
||||
}
|
||||
end = chars[i];
|
||||
}
|
||||
|
||||
regions.Add(new CharacterRegion(start, end));
|
||||
|
||||
return regions.ToArray();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
for (int index = 0; index < value.Length; ++index)
|
||||
{
|
||||
CharacterRegion characterRegion = value[index];
|
||||
if (characterRegion.End < characterRegion.Start)
|
||||
throw new ArgumentException("CharacterRegion.End must be greater than CharacterRegion.Start");
|
||||
|
||||
for (var start = characterRegion.Start; start <= characterRegion.End; start++)
|
||||
Characters.Add(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ContentSerializerIgnore]
|
||||
public ICollection<char> Characters
|
||||
{
|
||||
get { return characters; }
|
||||
internal set { characters = new CharacterCollection(value); }
|
||||
}
|
||||
|
||||
internal FontDescription()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of FontDescription and initializes its members to the specified font, size, and spacing, using FontDescriptionStyle.Regular as the default value for Style.
|
||||
/// </summary>
|
||||
/// <param name="fontName">The name of the font, such as Times New Roman.</param>
|
||||
/// <param name="size">The size, in points, of the font.</param>
|
||||
/// <param name="spacing">The amount of space, in pixels, to insert between letters in a string.</param>
|
||||
public FontDescription(string fontName, float size, float spacing)
|
||||
: this(fontName, size, spacing, FontDescriptionStyle.Regular, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of FontDescription and initializes its members to the specified font, size, spacing, and style.
|
||||
/// </summary>
|
||||
/// <param name="fontName">The name of the font, such as Times New Roman.</param>
|
||||
/// <param name="size">The size, in points, of the font.</param>
|
||||
/// <param name="spacing">The amount of space, in pixels, to insert between letters in a string.</param>
|
||||
/// <param name="fontStyle">The font style for the font.</param>
|
||||
public FontDescription(string fontName, float size, float spacing, FontDescriptionStyle fontStyle)
|
||||
: this(fontName, size, spacing, fontStyle, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of FontDescription using the specified values.
|
||||
/// </summary>
|
||||
/// <param name="fontName">The name of the font, such as Times New Roman.</param>
|
||||
/// <param name="size">The size, in points, of the font.</param>
|
||||
/// <param name="spacing">The amount of space, in pixels, to insert between letters in a string.</param>
|
||||
/// <param name="fontStyle">The font style for the font.</param>
|
||||
/// <param name="useKerning">true if kerning information is used when drawing characters; false otherwise.</param>
|
||||
public FontDescription(string fontName, float size, float spacing, FontDescriptionStyle fontStyle, bool useKerning)
|
||||
{
|
||||
// Write to the properties so the validation is run
|
||||
FontName = fontName;
|
||||
Size = size;
|
||||
Spacing = spacing;
|
||||
Style = fontStyle;
|
||||
UseKerning = useKerning;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags that describe style information to be applied to text.
|
||||
/// You can combine these flags by using a bitwise OR operator (|).
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FontDescriptionStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Bold text.
|
||||
/// </summary>
|
||||
Bold,
|
||||
|
||||
/// <summary>
|
||||
/// Italic text.
|
||||
/// </summary>
|
||||
Italic,
|
||||
|
||||
/// <summary>
|
||||
/// Normal text.
|
||||
/// </summary>
|
||||
Regular,
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties that define various aspects of a geometry batch.
|
||||
/// </summary>
|
||||
public class GeometryContent : ContentItem
|
||||
{
|
||||
IndexCollection indices;
|
||||
MaterialContent material;
|
||||
MeshContent parent;
|
||||
VertexContent vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of triangle indices for this geometry batch. Geometry is stored as an indexed triangle list, where each group of three indices defines a single triangle.
|
||||
/// </summary>
|
||||
public IndexCollection Indices
|
||||
{
|
||||
get
|
||||
{
|
||||
return indices;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material of the parent mesh.
|
||||
/// </summary>
|
||||
public MaterialContent Material
|
||||
{
|
||||
get
|
||||
{
|
||||
return material;
|
||||
}
|
||||
set
|
||||
{
|
||||
material = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent MeshContent for this object.
|
||||
/// </summary>
|
||||
public MeshContent Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of vertex batches for the geometry batch.
|
||||
/// </summary>
|
||||
public VertexContent Vertices
|
||||
{
|
||||
get
|
||||
{
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of GeometryContent.
|
||||
/// </summary>
|
||||
public GeometryContent()
|
||||
{
|
||||
indices = new IndexCollection();
|
||||
vertices = new VertexContent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for maintaining a collection of geometry batches that make up a mesh.
|
||||
/// </summary>
|
||||
public sealed class GeometryContentCollection : ChildCollection<MeshContent, GeometryContent>
|
||||
{
|
||||
internal GeometryContentCollection(MeshContent parent)
|
||||
: base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of a child object.
|
||||
/// </summary>
|
||||
/// <param name="child">The child of the parent being retrieved.</param>
|
||||
/// <returns>The parent of the child object.</returns>
|
||||
protected override MeshContent GetParent(GeometryContent child)
|
||||
{
|
||||
return child.Parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent of the specified child object.
|
||||
/// </summary>
|
||||
/// <param name="child">The child of the parent being set.</param>
|
||||
/// <param name="parent">The parent of the child object.</param>
|
||||
protected override void SetParent(GeometryContent child, MeshContent parent)
|
||||
{
|
||||
child.Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
// 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.IO;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Graphics.PackedVector;
|
||||
using FreeImageAPI;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public static class GraphicsUtil
|
||||
{
|
||||
internal static BitmapContent Resize(this BitmapContent bitmap, int newWidth, int newHeight)
|
||||
{
|
||||
BitmapContent src = bitmap;
|
||||
SurfaceFormat format;
|
||||
src.TryGetFormat(out format);
|
||||
if (format != SurfaceFormat.Vector4)
|
||||
{
|
||||
var v4 = new PixelBitmapContent<Vector4>(src.Width, src.Height);
|
||||
BitmapContent.Copy(src, v4);
|
||||
src = v4;
|
||||
}
|
||||
|
||||
// Convert to FreeImage bitmap
|
||||
var bytes = src.GetPixelData();
|
||||
var fi = FreeImage.ConvertFromRawBits(bytes, FREE_IMAGE_TYPE.FIT_RGBAF, src.Width, src.Height, SurfaceFormat.Vector4.GetSize() * src.Width, 128, 0, 0, 0, true);
|
||||
|
||||
// Resize
|
||||
var newfi = FreeImage.Rescale(fi, newWidth, newHeight, FREE_IMAGE_FILTER.FILTER_BICUBIC);
|
||||
FreeImage.UnloadEx(ref fi);
|
||||
|
||||
// Convert back to PixelBitmapContent<Vector4>
|
||||
src = new PixelBitmapContent<Vector4>(newWidth, newHeight);
|
||||
bytes = new byte[SurfaceFormat.Vector4.GetSize() * newWidth * newHeight];
|
||||
FreeImage.ConvertToRawBits(bytes, newfi, SurfaceFormat.Vector4.GetSize() * newWidth, 128, 0, 0, 0, true);
|
||||
src.SetPixelData(bytes);
|
||||
FreeImage.UnloadEx(ref newfi);
|
||||
// Convert back to source type if required
|
||||
if (format != SurfaceFormat.Vector4)
|
||||
{
|
||||
var s = (BitmapContent)Activator.CreateInstance(bitmap.GetType(), new object[] { newWidth, newHeight });
|
||||
BitmapContent.Copy(src, s);
|
||||
src = s;
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
public static void BGRAtoRGBA(byte[] data)
|
||||
{
|
||||
for (var x = 0; x < data.Length; x += 4)
|
||||
{
|
||||
data[x] ^= data[x + 2];
|
||||
data[x + 2] ^= data[x];
|
||||
data[x] ^= data[x + 2];
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPowerOfTwo(int x)
|
||||
{
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the next power of two. Returns same value if already is PoT.
|
||||
/// </summary>
|
||||
public static int GetNextPowerOfTwo(int value)
|
||||
{
|
||||
if (IsPowerOfTwo(value))
|
||||
return value;
|
||||
|
||||
var nearestPower = 1;
|
||||
while (nearestPower < value)
|
||||
nearestPower = nearestPower << 1;
|
||||
|
||||
return nearestPower;
|
||||
}
|
||||
|
||||
enum AlphaRange
|
||||
{
|
||||
/// <summary>
|
||||
/// Pixel data has no alpha values below 1.0.
|
||||
/// </summary>
|
||||
Opaque,
|
||||
|
||||
/// <summary>
|
||||
/// Pixel data contains alpha values that are either 0.0 or 1.0.
|
||||
/// </summary>
|
||||
Cutout,
|
||||
|
||||
/// <summary>
|
||||
/// Pixel data contains alpha values that cover the full range of 0.0 to 1.0.
|
||||
/// </summary>
|
||||
Full,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alpha range in a set of pixels.
|
||||
/// </summary>
|
||||
/// <param name="bitmap">A bitmap of full-colour floating point pixel data in RGBA or BGRA order.</param>
|
||||
/// <returns>A member of the AlphaRange enum to describe the range of alpha in the pixel data.</returns>
|
||||
static AlphaRange CalculateAlphaRange(BitmapContent bitmap)
|
||||
{
|
||||
AlphaRange result = AlphaRange.Opaque;
|
||||
var pixelBitmap = bitmap as PixelBitmapContent<Vector4>;
|
||||
if (pixelBitmap != null)
|
||||
{
|
||||
for (int y = 0; y < pixelBitmap.Height; ++y)
|
||||
{
|
||||
var row = pixelBitmap.GetRow(y);
|
||||
foreach (var pixel in row)
|
||||
{
|
||||
if (pixel.W == 0.0)
|
||||
result = AlphaRange.Cutout;
|
||||
else if (pixel.W < 1.0)
|
||||
return AlphaRange.Full;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void CompressPvrtc(ContentProcessorContext context, TextureContent content, bool isSpriteFont)
|
||||
{
|
||||
// If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR
|
||||
if (isSpriteFont)
|
||||
{
|
||||
CompressColor16Bit(context, content);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate number of mip levels
|
||||
var width = content.Faces[0][0].Height;
|
||||
var height = content.Faces[0][0].Width;
|
||||
|
||||
if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height) || (width != height))
|
||||
{
|
||||
context.Logger.LogWarning(null, content.Identity, "PVR compression requires width and height to be powers of two and equal. Falling back to 16-bit color.");
|
||||
CompressColor16Bit(context, content);
|
||||
return;
|
||||
}
|
||||
|
||||
var face = content.Faces[0][0];
|
||||
|
||||
var alphaRange = CalculateAlphaRange(face);
|
||||
|
||||
if (alphaRange == AlphaRange.Opaque)
|
||||
content.ConvertBitmapType(typeof(PvrtcRgb4BitmapContent));
|
||||
else
|
||||
content.ConvertBitmapType(typeof(PvrtcRgba4BitmapContent));
|
||||
}
|
||||
|
||||
public static void CompressDxt(ContentProcessorContext context, TextureContent content, bool isSpriteFont)
|
||||
{
|
||||
var face = content.Faces[0][0];
|
||||
|
||||
if (context.TargetProfile == GraphicsProfile.Reach)
|
||||
{
|
||||
if (!IsPowerOfTwo(face.Width) || !IsPowerOfTwo(face.Height))
|
||||
throw new PipelineException("DXT compression requires width and height must be powers of two in Reach graphics profile.");
|
||||
}
|
||||
|
||||
// Test the alpha channel to figure out if we have alpha.
|
||||
var alphaRange = CalculateAlphaRange(face);
|
||||
|
||||
// TODO: This isn't quite right.
|
||||
//
|
||||
// We should be generating DXT1 textures for cutout alpha
|
||||
// as DXT1 supports 1bit alpha and it uses less memory.
|
||||
//
|
||||
// XNA never generated DXT3 for textures... it always picked
|
||||
// between DXT1 for cutouts and DXT5 for fractional alpha.
|
||||
//
|
||||
// DXT3 however can produce better results for high frequency
|
||||
// alpha like a chain link fence where is DXT5 is better for
|
||||
// low frequency alpha like clouds. I don't know how we can
|
||||
// pick the right thing in this case without a hint.
|
||||
//
|
||||
if (isSpriteFont)
|
||||
CompressFontDXT3(content);
|
||||
else if (alphaRange == AlphaRange.Opaque)
|
||||
content.ConvertBitmapType(typeof(Dxt1BitmapContent));
|
||||
else if (alphaRange == AlphaRange.Cutout)
|
||||
content.ConvertBitmapType(typeof(Dxt3BitmapContent));
|
||||
else
|
||||
content.ConvertBitmapType(typeof(Dxt5BitmapContent));
|
||||
}
|
||||
|
||||
static public void CompressAti(ContentProcessorContext context, TextureContent content, bool isSpriteFont)
|
||||
{
|
||||
// If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR
|
||||
if (isSpriteFont)
|
||||
{
|
||||
CompressColor16Bit(context, content);
|
||||
return;
|
||||
}
|
||||
|
||||
var face = content.Faces[0][0];
|
||||
var alphaRange = CalculateAlphaRange(face);
|
||||
|
||||
if (alphaRange == AlphaRange.Full)
|
||||
content.ConvertBitmapType(typeof(AtcExplicitBitmapContent));
|
||||
else
|
||||
content.ConvertBitmapType(typeof(AtcInterpolatedBitmapContent));
|
||||
}
|
||||
|
||||
static public void CompressEtc1(ContentProcessorContext context, TextureContent content, bool isSpriteFont)
|
||||
{
|
||||
// If sharp alpha is required (for a font texture page), use 16-bit color instead of PVR
|
||||
if (isSpriteFont)
|
||||
{
|
||||
CompressColor16Bit(context, content);
|
||||
return;
|
||||
}
|
||||
|
||||
var face = content.Faces[0][0];
|
||||
var alphaRange = CalculateAlphaRange(face);
|
||||
|
||||
// Use BGRA4444 for textures with non-opaque alpha values
|
||||
if (alphaRange != AlphaRange.Opaque)
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Bgra4444>));
|
||||
else
|
||||
{
|
||||
// PVR SGX does not handle non-POT ETC1 textures.
|
||||
// https://code.google.com/p/libgdx/issues/detail?id=1310
|
||||
// Since we already enforce POT for PVR and DXT in Reach, we will also enforce POT for ETC1
|
||||
if (!IsPowerOfTwo(face.Width) || !IsPowerOfTwo(face.Height))
|
||||
{
|
||||
context.Logger.LogWarning(null, content.Identity, "ETC1 compression requires width and height to be powers of two due to hardware restrictions on some devices. Falling back to BGR565.");
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Bgr565>));
|
||||
}
|
||||
else
|
||||
content.ConvertBitmapType(typeof(Etc1BitmapContent));
|
||||
}
|
||||
}
|
||||
|
||||
static public void CompressColor16Bit(ContentProcessorContext context, TextureContent content)
|
||||
{
|
||||
var face = content.Faces[0][0];
|
||||
var alphaRange = CalculateAlphaRange(face);
|
||||
|
||||
if (alphaRange == AlphaRange.Opaque)
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Bgr565>));
|
||||
else if (alphaRange == AlphaRange.Cutout)
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Bgra5551>));
|
||||
else
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Bgra4444>));
|
||||
}
|
||||
|
||||
// Compress the greyscale font texture page using a specially-formulated DXT3 mode
|
||||
static public unsafe void CompressFontDXT3(TextureContent content)
|
||||
{
|
||||
if (content.Faces.Count > 1)
|
||||
throw new PipelineException("Font textures should only have one face");
|
||||
|
||||
var block = new Vector4[16];
|
||||
for (int i = 0; i < content.Faces[0].Count; ++i)
|
||||
{
|
||||
var face = content.Faces[0][i];
|
||||
var xBlocks = (face.Width + 3) / 4;
|
||||
var yBlocks = (face.Height + 3) / 4;
|
||||
var dxt3Size = xBlocks * yBlocks * 16;
|
||||
var buffer = new byte[dxt3Size];
|
||||
|
||||
var bytes = face.GetPixelData();
|
||||
fixed (byte* b = bytes)
|
||||
{
|
||||
Vector4* colors = (Vector4*)b;
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
while (h < (face.Height & ~3))
|
||||
{
|
||||
w = 0;
|
||||
x = 0;
|
||||
|
||||
var h0 = h * face.Width;
|
||||
var h1 = h0 + face.Width;
|
||||
var h2 = h1 + face.Width;
|
||||
var h3 = h2 + face.Width;
|
||||
|
||||
while (w < (face.Width & ~3))
|
||||
{
|
||||
block[0] = colors[w + h0];
|
||||
block[1] = colors[w + h0 + 1];
|
||||
block[2] = colors[w + h0 + 2];
|
||||
block[3] = colors[w + h0 + 3];
|
||||
block[4] = colors[w + h1];
|
||||
block[5] = colors[w + h1 + 1];
|
||||
block[6] = colors[w + h1 + 2];
|
||||
block[7] = colors[w + h1 + 3];
|
||||
block[8] = colors[w + h2];
|
||||
block[9] = colors[w + h2 + 1];
|
||||
block[10] = colors[w + h2 + 2];
|
||||
block[11] = colors[w + h2 + 3];
|
||||
block[12] = colors[w + h3];
|
||||
block[13] = colors[w + h3 + 1];
|
||||
block[14] = colors[w + h3 + 2];
|
||||
block[15] = colors[w + h3 + 3];
|
||||
|
||||
int offset = (x + y * xBlocks) * 16;
|
||||
CompressFontDXT3Block(block, buffer, offset);
|
||||
|
||||
w += 4;
|
||||
++x;
|
||||
}
|
||||
|
||||
// Do partial block at end of row
|
||||
if (w < face.Width)
|
||||
{
|
||||
var cols = face.Width - w;
|
||||
Array.Clear(block, 0, 16);
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
h0 = (h + r) * face.Width;
|
||||
for (int c = 0; c < cols; ++c)
|
||||
block[(r * 4) + c] = colors[w + h0 + c];
|
||||
}
|
||||
|
||||
int offset = (x + y * xBlocks) * 16;
|
||||
CompressFontDXT3Block(block, buffer, offset);
|
||||
}
|
||||
|
||||
h += 4;
|
||||
++y;
|
||||
}
|
||||
|
||||
// Do last partial row
|
||||
if (h < face.Height)
|
||||
{
|
||||
var rows = face.Height - h;
|
||||
w = 0;
|
||||
x = 0;
|
||||
while (w < (face.Width & ~3))
|
||||
{
|
||||
Array.Clear(block, 0, 16);
|
||||
for (int r = 0; r < rows; ++r)
|
||||
{
|
||||
var h0 = (h + r) * face.Width;
|
||||
block[(r * 4) + 0] = colors[w + h0 + 0];
|
||||
block[(r * 4) + 1] = colors[w + h0 + 1];
|
||||
block[(r * 4) + 2] = colors[w + h0 + 2];
|
||||
block[(r * 4) + 3] = colors[w + h0 + 3];
|
||||
}
|
||||
|
||||
int offset = (x + y * xBlocks) * 16;
|
||||
CompressFontDXT3Block(block, buffer, offset);
|
||||
|
||||
w += 4;
|
||||
++x;
|
||||
}
|
||||
|
||||
// Do last partial block
|
||||
if (w < face.Width)
|
||||
{
|
||||
var cols = face.Width - w;
|
||||
Array.Clear(block, 0, 16);
|
||||
for (int r = 0; r < rows; ++r)
|
||||
{
|
||||
var h0 = (h + r) * face.Width;
|
||||
for (int c = 0; c < cols; ++c)
|
||||
block[(r * 4) + c] = colors[w + h0 + c];
|
||||
}
|
||||
|
||||
int offset = (x + y * xBlocks) * 16;
|
||||
CompressFontDXT3Block(block, buffer, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var dxt3 = new Dxt3BitmapContent(face.Width, face.Height);
|
||||
dxt3.SetPixelData(buffer);
|
||||
content.Faces[0][i] = dxt3;
|
||||
}
|
||||
}
|
||||
|
||||
// Maps a 2-bit greyscale to the index required for DXT3
|
||||
// 00 = color0
|
||||
// 01 = color1
|
||||
// 10 = 2/3 * color0 + 1/3 * color1
|
||||
// 11 = 1/3 * color0 + 2/3 * color1
|
||||
static byte[] dxt3Map = new byte[] { 0, 2, 3, 1 };
|
||||
|
||||
// Compress a single 4x4 block from colors into buffer at the given offset
|
||||
static void CompressFontDXT3Block(Vector4[] colors, byte[] buffer, int offset)
|
||||
{
|
||||
// Get the alpha into a 0-15 range
|
||||
int a0 = (int)(colors[0].W * 15.0);
|
||||
int a1 = (int)(colors[1].W * 15.0);
|
||||
int a2 = (int)(colors[2].W * 15.0);
|
||||
int a3 = (int)(colors[3].W * 15.0);
|
||||
int a4 = (int)(colors[4].W * 15.0);
|
||||
int a5 = (int)(colors[5].W * 15.0);
|
||||
int a6 = (int)(colors[6].W * 15.0);
|
||||
int a7 = (int)(colors[7].W * 15.0);
|
||||
int a8 = (int)(colors[8].W * 15.0);
|
||||
int a9 = (int)(colors[9].W * 15.0);
|
||||
int a10 = (int)(colors[10].W * 15.0);
|
||||
int a11 = (int)(colors[11].W * 15.0);
|
||||
int a12 = (int)(colors[12].W * 15.0);
|
||||
int a13 = (int)(colors[13].W * 15.0);
|
||||
int a14 = (int)(colors[14].W * 15.0);
|
||||
int a15 = (int)(colors[15].W * 15.0);
|
||||
|
||||
// Duplicate the top two bits into the bottom two bits so we get one of four values: b0000, b0101, b1010, b1111
|
||||
a0 = (a0 & 0xC) | (a0 >> 2);
|
||||
a1 = (a1 & 0xC) | (a1 >> 2);
|
||||
a2 = (a2 & 0xC) | (a2 >> 2);
|
||||
a3 = (a3 & 0xC) | (a3 >> 2);
|
||||
a4 = (a4 & 0xC) | (a4 >> 2);
|
||||
a5 = (a5 & 0xC) | (a5 >> 2);
|
||||
a6 = (a6 & 0xC) | (a6 >> 2);
|
||||
a7 = (a7 & 0xC) | (a7 >> 2);
|
||||
a8 = (a8 & 0xC) | (a8 >> 2);
|
||||
a9 = (a9 & 0xC) | (a9 >> 2);
|
||||
a10 = (a10 & 0xC) | (a10 >> 2);
|
||||
a11 = (a11 & 0xC) | (a11 >> 2);
|
||||
a12 = (a12 & 0xC) | (a12 >> 2);
|
||||
a13 = (a13 & 0xC) | (a13 >> 2);
|
||||
a14 = (a14 & 0xC) | (a14 >> 2);
|
||||
a15 = (a15 & 0xC) | (a15 >> 2);
|
||||
|
||||
// 4-bit alpha
|
||||
buffer[offset + 0] = (byte)((a1 << 4) | a0);
|
||||
buffer[offset + 1] = (byte)((a3 << 4) | a2);
|
||||
buffer[offset + 2] = (byte)((a5 << 4) | a4);
|
||||
buffer[offset + 3] = (byte)((a7 << 4) | a6);
|
||||
buffer[offset + 4] = (byte)((a9 << 4) | a8);
|
||||
buffer[offset + 5] = (byte)((a11 << 4) | a10);
|
||||
buffer[offset + 6] = (byte)((a13 << 4) | a12);
|
||||
buffer[offset + 7] = (byte)((a15 << 4) | a14);
|
||||
|
||||
// color0 (transparent)
|
||||
buffer[offset + 8] = 0;
|
||||
buffer[offset + 9] = 0;
|
||||
|
||||
// color1 (white)
|
||||
buffer[offset + 10] = 255;
|
||||
buffer[offset + 11] = 255;
|
||||
|
||||
// Get the red (to be used for green and blue channels as well) into a 0-15 range
|
||||
a0 = (int)(colors[0].X * 15.0);
|
||||
a1 = (int)(colors[1].X * 15.0);
|
||||
a2 = (int)(colors[2].X * 15.0);
|
||||
a3 = (int)(colors[3].X * 15.0);
|
||||
a4 = (int)(colors[4].X * 15.0);
|
||||
a5 = (int)(colors[5].X * 15.0);
|
||||
a6 = (int)(colors[6].X * 15.0);
|
||||
a7 = (int)(colors[7].X * 15.0);
|
||||
a8 = (int)(colors[8].X * 15.0);
|
||||
a9 = (int)(colors[9].X * 15.0);
|
||||
a10 = (int)(colors[10].X * 15.0);
|
||||
a11 = (int)(colors[11].X * 15.0);
|
||||
a12 = (int)(colors[12].X * 15.0);
|
||||
a13 = (int)(colors[13].X * 15.0);
|
||||
a14 = (int)(colors[14].X * 15.0);
|
||||
a15 = (int)(colors[15].X * 15.0);
|
||||
|
||||
// Duplicate the top two bits into the bottom two bits so we get one of four values: b0000, b0101, b1010, b1111
|
||||
a0 = (a0 & 0xC) | (a0 >> 2);
|
||||
a1 = (a1 & 0xC) | (a1 >> 2);
|
||||
a2 = (a2 & 0xC) | (a2 >> 2);
|
||||
a3 = (a3 & 0xC) | (a3 >> 2);
|
||||
a4 = (a4 & 0xC) | (a4 >> 2);
|
||||
a5 = (a5 & 0xC) | (a5 >> 2);
|
||||
a6 = (a6 & 0xC) | (a6 >> 2);
|
||||
a7 = (a7 & 0xC) | (a7 >> 2);
|
||||
a8 = (a8 & 0xC) | (a8 >> 2);
|
||||
a9 = (a9 & 0xC) | (a9 >> 2);
|
||||
a10 = (a10 & 0xC) | (a10 >> 2);
|
||||
a11 = (a11 & 0xC) | (a11 >> 2);
|
||||
a12 = (a12 & 0xC) | (a12 >> 2);
|
||||
a13 = (a13 & 0xC) | (a13 >> 2);
|
||||
a14 = (a14 & 0xC) | (a14 >> 2);
|
||||
a15 = (a15 & 0xC) | (a15 >> 2);
|
||||
|
||||
// Color indices (00 = color0, 01 = color1, 10 = 2/3 * color0 + 1/3 * color1, 11 = 1/3 * color0 + 2/3 * color1)
|
||||
buffer[offset + 12] = (byte)((dxt3Map[a3 >> 2] << 6) | (dxt3Map[a2 >> 2] << 4) | (dxt3Map[a1 >> 2] << 2) | dxt3Map[a0 >> 2]);
|
||||
buffer[offset + 13] = (byte)((dxt3Map[a7 >> 2] << 6) | (dxt3Map[a6 >> 2] << 4) | (dxt3Map[a5 >> 2] << 2) | dxt3Map[a4 >> 2]);
|
||||
buffer[offset + 14] = (byte)((dxt3Map[a11 >> 2] << 6) | (dxt3Map[a10 >> 2] << 4) | (dxt3Map[a9 >> 2] << 2) | dxt3Map[a8 >> 2]);
|
||||
buffer[offset + 15] = (byte)((dxt3Map[a15 >> 2] << 6) | (dxt3Map[a14 >> 2] << 4) | (dxt3Map[a13 >> 2] << 2) | dxt3Map[a12 >> 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for maintaining a list of index values.
|
||||
/// </summary>
|
||||
public sealed class IndexCollection : Collection<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of IndexCollection.
|
||||
/// </summary>
|
||||
public IndexCollection()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a range of indices to the collection.
|
||||
/// </summary>
|
||||
/// <param name="indices">A collection of indices to add.</param>
|
||||
public void AddRange(IEnumerable<int> indices)
|
||||
{
|
||||
foreach (var t in indices)
|
||||
Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for maintaining a list of vertex positions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is designed to collect the vertex positions for a VertexContent object. Use the contents
|
||||
/// of the PositionIndices property (of the contained VertexContent object) to index into the Positions
|
||||
/// property of the parent mesh.
|
||||
/// </remarks>
|
||||
public sealed class IndirectPositionCollection : IList<Vector3>
|
||||
{
|
||||
private readonly VertexChannel<int> _positionIndices;
|
||||
private readonly GeometryContent _geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Number of positions in the collection.
|
||||
/// </summary>
|
||||
/// <value>Number of positions.</value>
|
||||
public int Count
|
||||
{
|
||||
get { return _positionIndices.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position at the specified index.
|
||||
/// </summary>
|
||||
/// <value>Position located at index.</value>
|
||||
public Vector3 this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var remap = _positionIndices[index];
|
||||
return _geometry.Parent.Positions[remap];
|
||||
}
|
||||
set
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this object is read-only.
|
||||
/// </summary>
|
||||
/// <value>true if this object is read-only; false otherwise.</value>
|
||||
bool ICollection<Vector3>.IsReadOnly
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of IndirectPositionCollection.
|
||||
/// </summary>
|
||||
internal IndirectPositionCollection(GeometryContent geom, VertexChannel<int> positionIndices)
|
||||
{
|
||||
_geometry = geom;
|
||||
_positionIndices = positionIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified position is in the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">Position being searched for in the collection.</param>
|
||||
/// <returns>true if the position was found; false otherwise.</returns>
|
||||
public bool Contains(Vector3 item)
|
||||
{
|
||||
return IndexOf(item) > -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the specified positions to an array, starting at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="array">Array of positions to be copied.</param>
|
||||
/// <param name="arrayIndex">Index of the first copied position.</param>
|
||||
public void CopyTo(Vector3[] array, int arrayIndex)
|
||||
{
|
||||
foreach (var vec in this)
|
||||
array[arrayIndex++] = vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator interface for reading the position values.
|
||||
/// </summary>
|
||||
/// <returns>Interface for enumerating the collection of position values.</returns>
|
||||
public IEnumerator<Vector3> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
yield return this[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified position in a collection.
|
||||
/// </summary>
|
||||
/// <param name="item">Position being searched for.</param>
|
||||
/// <returns>Index of the specified position or -1 if not found.</returns>
|
||||
public int IndexOf(Vector3 item)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
if (this[i] == item)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal Exception Readonly()
|
||||
{
|
||||
return new NotSupportedException("The collection is read only!");
|
||||
}
|
||||
|
||||
void ICollection<Vector3>.Add(Vector3 item)
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
|
||||
void ICollection<Vector3>.Clear()
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
|
||||
bool ICollection<Vector3>.Remove(Vector3 item)
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
|
||||
void IList<Vector3>.Insert(int index, Vector3 item)
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
|
||||
void IList<Vector3>.RemoveAt(int index)
|
||||
{
|
||||
throw Readonly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that can iterate through the collection.
|
||||
/// </summary>
|
||||
/// <returns>Enumerator that can iterate through the collection.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#region File Description
|
||||
//-----------------------------------------------------------------------------
|
||||
// LocalizedFontDescription.cs
|
||||
//
|
||||
// Microsoft XNA Community Game Platform
|
||||
// Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region Using Statements
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
|
||||
#endregion
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Normally, when you add a .spritefont file to your project, this data is
|
||||
/// deserialized into a FontDescription object, which is then built into a
|
||||
/// SpriteFontContent by the FontDescriptionProcessor. But to localize the
|
||||
/// font, we want to add some additional data, so our custom processor can
|
||||
/// know what .resx files it needs to scan. We do this by defining our own
|
||||
/// custom font description class, deriving from the built in FontDescription
|
||||
/// type, and adding a new property to store the resource filenames.
|
||||
/// </summary>
|
||||
public class LocalizedFontDescription : FontDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public LocalizedFontDescription()
|
||||
: base("Arial", 14, 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add a new property to our font description, which will allow us to
|
||||
/// include a ResourceFiles element in the .spritefont XML. We use the
|
||||
/// ContentSerializer attribute to mark this as optional, so existing
|
||||
/// .spritefont files that do not include this ResourceFiles element
|
||||
/// can be imported as well.
|
||||
/// </summary>
|
||||
[ContentSerializer(Optional = true, CollectionItemName = "Resx")]
|
||||
public List<string> ResourceFiles
|
||||
{
|
||||
get { return resourceFiles; }
|
||||
}
|
||||
|
||||
List<string> resourceFiles = new List<string>();
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for maintaining a collection of named texture references.
|
||||
/// </summary>
|
||||
/// <remarks>In addition to texture references, opaque data values are stored in the OpaqueData property of the base class.</remarks>
|
||||
public class MaterialContent : ContentItem
|
||||
{
|
||||
readonly TextureReferenceDictionary _textures;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture collection of the material.
|
||||
/// </summary>
|
||||
/// <value>Collection of textures used by the material.</value>
|
||||
public TextureReferenceDictionary Textures { get { return _textures; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of MaterialContent.
|
||||
/// </summary>
|
||||
public MaterialContent()
|
||||
{
|
||||
_textures = new TextureReferenceDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference type from the OpaqueDataDictionary collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the related opaque data.</typeparam>
|
||||
/// <param name="key">Key of the property being retrieved.</param>
|
||||
/// <returns>The related opaque data.</returns>
|
||||
protected T GetReferenceTypeProperty<T>(string key) where T : class
|
||||
{
|
||||
object value;
|
||||
if (OpaqueData.TryGetValue(key, out value))
|
||||
return (T)value;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value from the Textures collection.
|
||||
/// </summary>
|
||||
/// <param name="key">Key of the texture being retrieved.</param>
|
||||
/// <returns>Reference to a texture from the collection.</returns>
|
||||
protected ExternalReference<TextureContent> GetTexture(string key)
|
||||
{
|
||||
ExternalReference<TextureContent> texture;
|
||||
_textures.TryGetValue(key, out texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value type from the OpaqueDataDictionary collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the value being retrieved.</typeparam>
|
||||
/// <param name="key">Key of the value type being retrieved.</param>
|
||||
/// <returns>Index of the value type beng retrieved.</returns>
|
||||
protected Nullable<T> GetValueTypeProperty<T>(string key) where T : struct
|
||||
{
|
||||
object value;
|
||||
if (OpaqueData.TryGetValue(key, out value))
|
||||
return (T)value;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value in the contained OpaqueDataDictionary object.
|
||||
/// If null is passed, the value is removed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the element being set.</typeparam>
|
||||
/// <param name="key">Name of the key being modified.</param>
|
||||
/// <param name="value">Value being set.</param>
|
||||
protected void SetProperty<T>(string key, T value)
|
||||
{
|
||||
if (value != null)
|
||||
OpaqueData[key] = value;
|
||||
else
|
||||
OpaqueData.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value in the contained TextureReferenceDictionary object.
|
||||
/// If null is passed, the value is removed.
|
||||
/// </summary>
|
||||
/// <param name="key">Name of the key being modified.</param>
|
||||
/// <param name="value">Value being set.</param>
|
||||
/// <remarks>The key value differs depending on the type of attached dictionary.
|
||||
/// If attached to a BasicMaterialContent dictionary (which becomes a BasicEffect object at run time), the value for the Texture key is used as the texture for the BasicEffect runtime object. Other keys are ignored.
|
||||
/// If attached to a EffectMaterialContent dictionary, key names are the texture names used by the effect. These names are dependent upon the author of the effect object.</remarks>
|
||||
protected void SetTexture(string key, ExternalReference<TextureContent> value)
|
||||
{
|
||||
if (value != null)
|
||||
_textures[key] = value;
|
||||
else
|
||||
_textures.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to make a copy of a material.
|
||||
/// </summary>
|
||||
/// <returns>A clone of the material.</returns>
|
||||
public MaterialContent Clone()
|
||||
{
|
||||
// Construct it via reflection.
|
||||
var clone = (MaterialContent)Activator.CreateInstance(GetType());
|
||||
|
||||
// Give it the same identity as the original material.
|
||||
clone.Name = Name;
|
||||
clone.Identity = Identity;
|
||||
|
||||
// Just copy the opaque data and textures which should
|
||||
// result in the same properties being set if the material
|
||||
// is implemented correctly.
|
||||
foreach (var pair in Textures)
|
||||
clone.Textures.Add(pair.Key, pair.Value);
|
||||
foreach (var pair in OpaqueData)
|
||||
clone.OpaqueData.Add(pair.Key, pair.Value);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public sealed class MeshBuilder
|
||||
{
|
||||
private readonly MeshContent _meshContent;
|
||||
|
||||
private MaterialContent _currentMaterial;
|
||||
private OpaqueDataDictionary _currentOpaqueData;
|
||||
private bool _geometryDirty;
|
||||
private GeometryContent _currentGeometryContent;
|
||||
|
||||
private readonly List<VertexChannel> _vertexChannels;
|
||||
private readonly List<object> _vertexChannelData;
|
||||
|
||||
private bool _finishedCreation;
|
||||
private bool _finishedMesh;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current value for position merging of the mesh.
|
||||
/// </summary>
|
||||
public bool MergeDuplicatePositions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tolerance for <see cref="MergeDuplicatePositions"/>.
|
||||
/// </summary>
|
||||
public float MergePositionTolerance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the current <see cref="MeshContent"/> object being processed.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _meshContent.Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_meshContent.Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the triangle winding order of the specified mesh.
|
||||
/// </summary>
|
||||
public bool SwapWindingOrder { get; set; }
|
||||
|
||||
|
||||
private MeshBuilder(string name)
|
||||
{
|
||||
_meshContent = new MeshContent();
|
||||
_vertexChannels = new List<VertexChannel>();
|
||||
_vertexChannelData = new List<object>();
|
||||
_currentGeometryContent = new GeometryContent();
|
||||
_currentOpaqueData = new OpaqueDataDictionary();
|
||||
_geometryDirty = true;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a vertex into the index collection.
|
||||
/// </summary>
|
||||
/// <param name="indexIntoVertexCollection">Index of the inserted vertex, in the collection.
|
||||
/// This corresponds to the value returned by <see cref="CreatePosition(float,float,float)"/>.</param>
|
||||
public void AddTriangleVertex(int indexIntoVertexCollection)
|
||||
{
|
||||
if (_finishedMesh)
|
||||
throw new InvalidOperationException( "This MeshBuilder can no longer be used because FinishMesh has been called.");
|
||||
|
||||
_finishedCreation = true;
|
||||
|
||||
if (_geometryDirty)
|
||||
{
|
||||
_currentGeometryContent = new GeometryContent();
|
||||
_currentGeometryContent.Material = _currentMaterial;
|
||||
foreach (var kvp in _currentOpaqueData)
|
||||
_currentGeometryContent.OpaqueData.Add(kvp.Key, kvp.Value);
|
||||
|
||||
// we have to copy our vertex channels to the new geometry
|
||||
foreach (var channel in _vertexChannels)
|
||||
{
|
||||
_currentGeometryContent.Vertices.Channels.Add(channel.Name, channel.ElementType, null);
|
||||
}
|
||||
_meshContent.Geometry.Add(_currentGeometryContent);
|
||||
_geometryDirty = false;
|
||||
|
||||
}
|
||||
// Add the vertex to the mesh and then add the vertex position to the indices list
|
||||
var pos = _currentGeometryContent.Vertices.Add(indexIntoVertexCollection);
|
||||
|
||||
// Then add the data for the other channels
|
||||
for (var i = 0; i < _vertexChannels.Count; i++)
|
||||
{
|
||||
var channel = _currentGeometryContent.Vertices.Channels[i];
|
||||
var data = _vertexChannelData[i];
|
||||
if (data == null)
|
||||
throw new InvalidOperationException(string.Format("Missing vertex channel data for channel {0}", channel.Name));
|
||||
|
||||
channel.Items.Add(data);
|
||||
}
|
||||
|
||||
_currentGeometryContent.Indices.Add(pos);
|
||||
}
|
||||
|
||||
public int CreateVertexChannel<T>(string usage)
|
||||
{
|
||||
if (_finishedMesh)
|
||||
throw new InvalidOperationException("This MeshBuilder can no longer be used because FinishMesh has been called.");
|
||||
if (_finishedCreation)
|
||||
throw new InvalidOperationException("Functions starting with 'Create' must be called before calling AddTriangleVertex");
|
||||
|
||||
var channel = new VertexChannel<T>(usage);
|
||||
_vertexChannels.Add(channel);
|
||||
_vertexChannelData.Add(default(T));
|
||||
|
||||
_currentGeometryContent.Vertices.Channels.Add<T>(usage, null);
|
||||
|
||||
return _vertexChannels.Count - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the specified vertex position into the vertex channel.
|
||||
/// </summary>
|
||||
/// <param name="x">Value of the x component of the vector.</param>
|
||||
/// <param name="y">Value of the y component of the vector.</param>
|
||||
/// <param name="z">Value of the z component of the vector.</param>
|
||||
/// <returns>Index of the inserted vertex.</returns>
|
||||
public int CreatePosition(float x, float y, float z)
|
||||
{
|
||||
return CreatePosition(new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the specified vertex position into the vertex channel at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="pos">Value of the vertex being inserted.</param>
|
||||
/// <returns>Index of the vertex being inserted.</returns>
|
||||
public int CreatePosition(Vector3 pos)
|
||||
{
|
||||
if (_finishedMesh)
|
||||
throw new InvalidOperationException( "This MeshBuilder can no longer be used because FinishMesh has been called.");
|
||||
if (_finishedCreation)
|
||||
throw new InvalidOperationException("Functions starting with 'Create' must be called before calling AddTriangleVertex");
|
||||
|
||||
_meshContent.Positions.Add(pos);
|
||||
return _meshContent.Positions.Count - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the creation of a mesh.
|
||||
/// </summary>
|
||||
/// <returns>Resultant mesh.</returns>
|
||||
public MeshContent FinishMesh()
|
||||
{
|
||||
if (_finishedMesh)
|
||||
return _meshContent;
|
||||
|
||||
if (MergeDuplicatePositions)
|
||||
MeshHelper.MergeDuplicatePositions(_meshContent, MergePositionTolerance);
|
||||
|
||||
MeshHelper.MergeDuplicateVertices(_meshContent);
|
||||
|
||||
MeshHelper.CalculateNormals(_meshContent, false);
|
||||
if (SwapWindingOrder)
|
||||
MeshHelper.SwapWindingOrder(_meshContent);
|
||||
|
||||
_finishedMesh = true;
|
||||
return _meshContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the material for the next triangles.
|
||||
/// </summary>
|
||||
/// <param name="material">Material for the next triangles.</param>
|
||||
/// <remarks>
|
||||
/// Sets the material for the triangles being defined next. This material
|
||||
/// and the opaque data dictionary, set with <see cref="SetOpaqueData"/>
|
||||
/// define the <see cref="GeometryContent"/> object containing the next
|
||||
/// triangles. When you set a new material or opaque data dictionary the
|
||||
/// triangles you add afterwards will belong to a new
|
||||
/// <see cref="GeometryContent"/> object.
|
||||
/// </remarks>
|
||||
public void SetMaterial(MaterialContent material)
|
||||
{
|
||||
if (_currentMaterial == material)
|
||||
return;
|
||||
|
||||
_currentMaterial = material;
|
||||
_geometryDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the opaque data for the next triangles.
|
||||
/// </summary>
|
||||
/// <param name="opaqueData">Opaque data dictionary for the next triangles.</param>
|
||||
/// <remarks>
|
||||
/// Sets the opaque data dictionary for the triangles being defined next. This dictionary
|
||||
/// and the material, set with <see cref="SetMaterial"/>, define the <see cref="GeometryContent"/>
|
||||
/// object containing the next triangles. When you set a new material or opaque data dictionary
|
||||
/// the triangles you add afterwards will belong to a new <see cref="GeometryContent"/> object.
|
||||
/// </remarks>
|
||||
public void SetOpaqueData(OpaqueDataDictionary opaqueData)
|
||||
{
|
||||
if (_currentOpaqueData == opaqueData)
|
||||
return;
|
||||
|
||||
_currentOpaqueData = opaqueData;
|
||||
_geometryDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified vertex data with new data.
|
||||
/// </summary>
|
||||
/// <param name="vertexDataIndex">Index of the vertex data channel being set. This should match the index returned by CreateVertexChannel.</param>
|
||||
/// <param name="channelData">New data values for the vertex data. The data type being set must match the data type for the vertex channel specified by vertexDataIndex.</param>
|
||||
public void SetVertexChannelData(int vertexDataIndex, object channelData)
|
||||
{
|
||||
if (_currentGeometryContent.Vertices.Channels[vertexDataIndex].ElementType != channelData.GetType())
|
||||
throw new InvalidOperationException(string.Format("Channel {0} data has a different type from input. Expected: {1}. Actual: {2}",
|
||||
vertexDataIndex, _currentGeometryContent.Vertices.Channels[vertexDataIndex].ElementType, channelData.GetType()));
|
||||
|
||||
_vertexChannelData[vertexDataIndex] = channelData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the creation of a mesh.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the mesh.</param>
|
||||
/// <returns>Object used when building the mesh.</returns>
|
||||
public static MeshBuilder StartMesh(string name)
|
||||
{
|
||||
return new MeshBuilder(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties and methods that define various aspects of a mesh.
|
||||
/// </summary>
|
||||
public class MeshContent : NodeContent
|
||||
{
|
||||
GeometryContentCollection geometry;
|
||||
PositionCollection positions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of geometry batches for the mesh.
|
||||
/// </summary>
|
||||
public GeometryContentCollection Geometry
|
||||
{
|
||||
get
|
||||
{
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of vertex position values.
|
||||
/// </summary>
|
||||
public PositionCollection Positions
|
||||
{
|
||||
get
|
||||
{
|
||||
return positions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of MeshContent.
|
||||
/// </summary>
|
||||
public MeshContent()
|
||||
{
|
||||
geometry = new GeometryContentCollection(this);
|
||||
positions = new PositionCollection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a transform directly to position and normal channels. Node transforms are unaffected.
|
||||
/// </summary>
|
||||
internal void TransformContents(ref Matrix xform)
|
||||
{
|
||||
// Transform positions
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
positions[i] = Vector3.Transform(positions[i], xform);
|
||||
|
||||
// Transform all vectors too:
|
||||
// Normals are "tangent covectors", which need to be transformed using the
|
||||
// transpose of the inverse matrix!
|
||||
Matrix inverseTranspose = Matrix.Transpose(Matrix.Invert(xform));
|
||||
foreach (var geom in geometry)
|
||||
{
|
||||
foreach (var channel in geom.Vertices.Channels)
|
||||
{
|
||||
var vector3Channel = channel as VertexChannel<Vector3>;
|
||||
if (vector3Channel == null)
|
||||
continue;
|
||||
|
||||
if (channel.Name.StartsWith("Normal") ||
|
||||
channel.Name.StartsWith("Binormal") ||
|
||||
channel.Name.StartsWith("Tangent"))
|
||||
{
|
||||
for (int i = 0; i < vector3Channel.Count; i++)
|
||||
{
|
||||
Vector3 normal = vector3Channel[i];
|
||||
Vector3.TransformNormal(ref normal, ref inverseTranspose, out normal);
|
||||
Vector3.Normalize(ref normal, out normal);
|
||||
vector3Channel[i] = normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap winding order when faces are mirrored.
|
||||
if (MeshHelper.IsLeftHanded(ref xform))
|
||||
MeshHelper.SwapWindingOrder(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
+712
@@ -0,0 +1,712 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public static class MeshHelper
|
||||
{
|
||||
static bool IsFinite(float v)
|
||||
{
|
||||
return !float.IsInfinity(v) && !float.IsNaN(v);
|
||||
}
|
||||
|
||||
static bool IsFinite(this Vector3 v)
|
||||
{
|
||||
return IsFinite(v.X) && IsFinite(v.Y) && IsFinite(v.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates vertex normals by accumulation of triangle face normals.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh which will recieve the normals.</param>
|
||||
/// <param name="overwriteExistingNormals">Overwrite or skip over geometry with existing normals.</param>
|
||||
/// <remarks>
|
||||
/// This calls <see cref="CalculateNormals(GeometryContent, bool)"/> to do the work.
|
||||
/// </remarks>
|
||||
public static void CalculateNormals(MeshContent mesh, bool overwriteExistingNormals)
|
||||
{
|
||||
foreach (var geom in mesh.Geometry)
|
||||
CalculateNormals(geom, overwriteExistingNormals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates vertex normals by accumulation of triangle face normals.
|
||||
/// </summary>
|
||||
/// <param name="geom">The geometry which will recieve the normals.</param>
|
||||
/// <param name="overwriteExistingNormals">Overwrite or skip over geometry with existing normals.</param>
|
||||
/// <remarks>
|
||||
/// We use a "Mean Weighted Equally" method generate vertex normals from triangle
|
||||
/// face normals. If normal cannot be calculated from the geometry we set it to zero.
|
||||
/// </remarks>
|
||||
public static void CalculateNormals(GeometryContent geom, bool overwriteExistingNormals)
|
||||
{
|
||||
VertexChannel<Vector3> channel;
|
||||
// Look for an existing normals channel.
|
||||
if (!geom.Vertices.Channels.Contains(VertexChannelNames.Normal()))
|
||||
{
|
||||
// We don't have existing normals, so add a new channel.
|
||||
channel = geom.Vertices.Channels.Add<Vector3>(VertexChannelNames.Normal(), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're not supposed to overwrite the existing
|
||||
// normals then we're done here.
|
||||
if (!overwriteExistingNormals)
|
||||
return;
|
||||
|
||||
channel = geom.Vertices.Channels.Get<Vector3>(VertexChannelNames.Normal());
|
||||
}
|
||||
|
||||
var positionIndices = geom.Vertices.PositionIndices;
|
||||
Debug.Assert(positionIndices.Count == channel.Count, "The position and channel sizes were different!");
|
||||
|
||||
// Accumulate all the triangle face normals for each vertex.
|
||||
var normals = new Vector3[positionIndices.Count];
|
||||
for (var i = 0; i < geom.Indices.Count; i += 3)
|
||||
{
|
||||
var ia = geom.Indices[i + 0];
|
||||
var ib = geom.Indices[i + 1];
|
||||
var ic = geom.Indices[i + 2];
|
||||
|
||||
var aa = geom.Vertices.Positions[ia];
|
||||
var bb = geom.Vertices.Positions[ib];
|
||||
var cc = geom.Vertices.Positions[ic];
|
||||
|
||||
var faceNormal = Vector3.Cross(cc - bb, bb - aa);
|
||||
var len = faceNormal.Length();
|
||||
if (len > 0.0f)
|
||||
{
|
||||
faceNormal = faceNormal / len;
|
||||
|
||||
// We are using the "Mean Weighted Equally" method where each
|
||||
// face has an equal weight in the final normal calculation.
|
||||
//
|
||||
// We could maybe switch to "Mean Weighted by Angle" which is said
|
||||
// to look best in most cases, but is more expensive to calculate.
|
||||
//
|
||||
// There is also an idea of weighting by triangle area, but IMO the
|
||||
// triangle area doesn't always have a direct relationship to the
|
||||
// shape of a mesh.
|
||||
//
|
||||
// For more ideas see:
|
||||
//
|
||||
// "A Comparison of Algorithms for Vertex Normal Computation"
|
||||
// by Shuangshuang Jin, Robert R. Lewis, David West.
|
||||
//
|
||||
|
||||
normals[positionIndices[ia]] += faceNormal;
|
||||
normals[positionIndices[ib]] += faceNormal;
|
||||
normals[positionIndices[ic]] += faceNormal;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the gathered vertex normals.
|
||||
for (var i = 0; i < normals.Length; i++)
|
||||
{
|
||||
var normal = normals[i];
|
||||
var len = normal.Length();
|
||||
if (len > 0.0f)
|
||||
normals[i] = normal / len;
|
||||
else
|
||||
{
|
||||
// TODO: It would be nice to be able to log this to
|
||||
// the pipeline so that it can be fixed in the model.
|
||||
|
||||
// TODO: We could maybe void this by a better algorithm
|
||||
// above for generating the normals.
|
||||
|
||||
// We have a zero length normal. You can argue that putting
|
||||
// anything here is better than nothing, but by leaving it to
|
||||
// zero it allows the caller to detect this and react to it.
|
||||
normals[i] = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new normals on the vertex channel.
|
||||
for (var i = 0; i < channel.Count; i++)
|
||||
channel[i] = normals[geom.Indices[i]];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the tangents and binormals (tangent frames) for each vertex in the mesh.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh which will have add tangent and binormal channels added.</param>
|
||||
/// <param name="textureCoordinateChannelName">The Vector2 texture coordinate channel used to generate tangent frames.</param>
|
||||
/// <param name="tangentChannelName"></param>
|
||||
/// <param name="binormalChannelName"></param>
|
||||
public static void CalculateTangentFrames(MeshContent mesh, string textureCoordinateChannelName, string tangentChannelName, string binormalChannelName)
|
||||
{
|
||||
foreach (var geom in mesh.Geometry)
|
||||
CalculateTangentFrames(geom, textureCoordinateChannelName, tangentChannelName, binormalChannelName);
|
||||
}
|
||||
|
||||
public static void CalculateTangentFrames(GeometryContent geom, string textureCoordinateChannelName, string tangentChannelName, string binormalChannelName)
|
||||
{
|
||||
var verts = geom.Vertices;
|
||||
var indices = geom.Indices;
|
||||
var channels = geom.Vertices.Channels;
|
||||
|
||||
var normals = channels.Get<Vector3>(VertexChannelNames.Normal(0));
|
||||
var uvs = channels.Get<Vector2>(textureCoordinateChannelName);
|
||||
|
||||
Vector3[] tangents, bitangents;
|
||||
CalculateTangentFrames(verts.Positions, indices, normals, uvs, out tangents, out bitangents);
|
||||
|
||||
// All the indices are 1:1 with the others, so we
|
||||
// can just add the new channels in place.
|
||||
|
||||
if (!string.IsNullOrEmpty(tangentChannelName))
|
||||
channels.Add(tangentChannelName, tangents);
|
||||
|
||||
if (!string.IsNullOrEmpty(binormalChannelName))
|
||||
channels.Add(binormalChannelName, bitangents);
|
||||
}
|
||||
|
||||
public static void CalculateTangentFrames(IList<Vector3> positions,
|
||||
IList<int> indices,
|
||||
IList<Vector3> normals,
|
||||
IList<Vector2> textureCoords,
|
||||
out Vector3[] tangents,
|
||||
out Vector3[] bitangents)
|
||||
{
|
||||
// Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”.
|
||||
// Terathon Software 3D Graphics Library, 2001.
|
||||
// http://www.terathon.com/code/tangent.html
|
||||
|
||||
// Hegde, Siddharth. "Messing with Tangent Space". Gamasutra, 2007.
|
||||
// http://www.gamasutra.com/view/feature/129939/messing_with_tangent_space.php
|
||||
|
||||
var numVerts = positions.Count;
|
||||
var numIndices = indices.Count;
|
||||
|
||||
var tan1 = new Vector3[numVerts];
|
||||
var tan2 = new Vector3[numVerts];
|
||||
|
||||
for (var index = 0; index < numIndices; index += 3)
|
||||
{
|
||||
var i1 = indices[index + 0];
|
||||
var i2 = indices[index + 1];
|
||||
var i3 = indices[index + 2];
|
||||
|
||||
var w1 = textureCoords[i1];
|
||||
var w2 = textureCoords[i2];
|
||||
var w3 = textureCoords[i3];
|
||||
|
||||
var s1 = w2.X - w1.X;
|
||||
var s2 = w3.X - w1.X;
|
||||
var t1 = w2.Y - w1.Y;
|
||||
var t2 = w3.Y - w1.Y;
|
||||
|
||||
var denom = s1 * t2 - s2 * t1;
|
||||
if (Math.Abs(denom) < float.Epsilon)
|
||||
{
|
||||
// The triangle UVs are zero sized one dimension.
|
||||
//
|
||||
// So we cannot calculate the vertex tangents for this
|
||||
// one trangle, but maybe it can with other trangles.
|
||||
continue;
|
||||
}
|
||||
|
||||
var r = 1.0f / denom;
|
||||
Debug.Assert(IsFinite(r), "Bad r!");
|
||||
|
||||
var v1 = positions[i1];
|
||||
var v2 = positions[i2];
|
||||
var v3 = positions[i3];
|
||||
|
||||
var x1 = v2.X - v1.X;
|
||||
var x2 = v3.X - v1.X;
|
||||
var y1 = v2.Y - v1.Y;
|
||||
var y2 = v3.Y - v1.Y;
|
||||
var z1 = v2.Z - v1.Z;
|
||||
var z2 = v3.Z - v1.Z;
|
||||
|
||||
var sdir = new Vector3()
|
||||
{
|
||||
X = (t2 * x1 - t1 * x2) * r,
|
||||
Y = (t2 * y1 - t1 * y2) * r,
|
||||
Z = (t2 * z1 - t1 * z2) * r,
|
||||
};
|
||||
|
||||
var tdir = new Vector3()
|
||||
{
|
||||
X = (s1 * x2 - s2 * x1) * r,
|
||||
Y = (s1 * y2 - s2 * y1) * r,
|
||||
Z = (s1 * z2 - s2 * z1) * r,
|
||||
};
|
||||
|
||||
tan1[i1] += sdir;
|
||||
Debug.Assert(tan1[i1].IsFinite(), "Bad tan1[i1]!");
|
||||
tan1[i2] += sdir;
|
||||
Debug.Assert(tan1[i2].IsFinite(), "Bad tan1[i2]!");
|
||||
tan1[i3] += sdir;
|
||||
Debug.Assert(tan1[i3].IsFinite(), "Bad tan1[i3]!");
|
||||
|
||||
tan2[i1] += tdir;
|
||||
Debug.Assert(tan2[i1].IsFinite(), "Bad tan2[i1]!");
|
||||
tan2[i2] += tdir;
|
||||
Debug.Assert(tan2[i2].IsFinite(), "Bad tan2[i2]!");
|
||||
tan2[i3] += tdir;
|
||||
Debug.Assert(tan2[i3].IsFinite(), "Bad tan2[i3]!");
|
||||
}
|
||||
|
||||
tangents = new Vector3[numVerts];
|
||||
bitangents = new Vector3[numVerts];
|
||||
|
||||
// At this point we have all the vectors accumulated, but we need to average
|
||||
// them all out. So we loop through all the final verts and do a Gram-Schmidt
|
||||
// orthonormalize, then make sure they're all unit length.
|
||||
for (var i = 0; i < numVerts; i++)
|
||||
{
|
||||
var n = normals[i];
|
||||
Debug.Assert(n.IsFinite(), "Bad normal!");
|
||||
Debug.Assert(n.Length() >= 0.9999f, "Bad normal!");
|
||||
|
||||
var t = tan1[i];
|
||||
if (t.LengthSquared() < float.Epsilon)
|
||||
{
|
||||
// TODO: Ideally we could spit out a warning to the
|
||||
// content logging here!
|
||||
|
||||
// We couldn't find a good tanget for this vertex.
|
||||
//
|
||||
// Rather than set them to zero which could produce
|
||||
// errors in other parts of the pipeline, we just take
|
||||
// a guess at something that may look ok.
|
||||
|
||||
t = Vector3.Cross(n, Vector3.UnitX);
|
||||
if (t.LengthSquared() < float.Epsilon)
|
||||
t = Vector3.Cross(n, Vector3.UnitY);
|
||||
|
||||
tangents[i] = Vector3.Normalize(t);
|
||||
bitangents[i] = Vector3.Cross(n, tangents[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Gram-Schmidt orthogonalize
|
||||
// TODO: This can be zero can cause NaNs on
|
||||
// normalize... how do we fix this?
|
||||
var tangent = t - n * Vector3.Dot(n, t);
|
||||
tangent = Vector3.Normalize(tangent);
|
||||
Debug.Assert(tangent.IsFinite(), "Bad tangent!");
|
||||
tangents[i] = tangent;
|
||||
|
||||
// Calculate handedness
|
||||
var w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0F) ? -1.0F : 1.0F;
|
||||
Debug.Assert(IsFinite(w), "Bad handedness!");
|
||||
|
||||
// Calculate the bitangent
|
||||
var bitangent = Vector3.Cross(n, tangent) * w;
|
||||
Debug.Assert(bitangent.IsFinite(), "Bad bitangent!");
|
||||
bitangents[i] = bitangent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for the root bone of the skeletion.
|
||||
/// </summary>
|
||||
/// <param name="node">The node from which to begin the search for the skeleton.</param>
|
||||
/// <returns>The root bone of the skeletion or null if none is found.</returns>
|
||||
public static BoneContent FindSkeleton(NodeContent node)
|
||||
{
|
||||
// We should always get a node to search!
|
||||
if (node == null)
|
||||
throw new ArgumentNullException("node");
|
||||
|
||||
// Search up thru the hierarchy.
|
||||
for (; node != null; node = node.Parent)
|
||||
{
|
||||
// First if this node is a bone then search up for the root.
|
||||
var root = node as BoneContent;
|
||||
if (root != null)
|
||||
{
|
||||
while (root.Parent is BoneContent)
|
||||
root = (BoneContent)root.Parent;
|
||||
return root;
|
||||
}
|
||||
|
||||
// Next try searching the children for a root bone.
|
||||
foreach (var nodeContent in node.Children)
|
||||
{
|
||||
var bone = nodeContent as BoneContent;
|
||||
if (bone == null)
|
||||
continue;
|
||||
|
||||
// If we found a bone
|
||||
if (root != null)
|
||||
throw new InvalidContentException("DuplicateSkeleton", node.Identity);
|
||||
|
||||
// This is our new root.
|
||||
root = bone;
|
||||
}
|
||||
|
||||
// If we found a root bone then return it, else
|
||||
// we continue the search to the node parent.
|
||||
if (root != null)
|
||||
return root;
|
||||
}
|
||||
|
||||
// We didn't find any bones!
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverses a skeleton depth-first and builds a list of its bones.
|
||||
/// </summary>
|
||||
public static IList<BoneContent> FlattenSkeleton(BoneContent skeleton)
|
||||
{
|
||||
if (skeleton == null)
|
||||
throw new ArgumentNullException("skeleton");
|
||||
|
||||
var results = new List<BoneContent>();
|
||||
var work = new Stack<NodeContent>(new[] { skeleton });
|
||||
while (work.Count > 0)
|
||||
{
|
||||
var top = work.Pop();
|
||||
var bone = top as BoneContent;
|
||||
if (bone != null)
|
||||
results.Add(bone);
|
||||
|
||||
for (var i = top.Children.Count - 1; i >= 0; i--)
|
||||
work.Push(top.Children[i]);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge any positions in the <see cref="PositionCollection"/> of the
|
||||
/// specified mesh that are at a distance less than the specified tolerance
|
||||
/// from each other.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to be processed.</param>
|
||||
/// <param name="tolerance">Tolerance value that determines how close
|
||||
/// positions must be to each other to be merged.</param>
|
||||
/// <remarks>
|
||||
/// This method will also update the <see cref="VertexContent.PositionIndices"/>
|
||||
/// in the <see cref="GeometryContent"/> of the specified mesh.
|
||||
/// </remarks>
|
||||
public static void MergeDuplicatePositions(MeshContent mesh, float tolerance)
|
||||
{
|
||||
if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
// TODO Improve performance with spatial partitioning scheme
|
||||
var indexLists = new List<IndexUpdateList>();
|
||||
foreach (var geom in mesh.Geometry)
|
||||
{
|
||||
var list = new IndexUpdateList(geom.Vertices.PositionIndices);
|
||||
indexLists.Add(list);
|
||||
}
|
||||
|
||||
for (var i = mesh.Positions.Count - 1; i >= 1; i--)
|
||||
{
|
||||
var pi = mesh.Positions[i];
|
||||
for (var j = i - 1; j >= 0; j--)
|
||||
{
|
||||
var pj = mesh.Positions[j];
|
||||
if (Vector3.Distance(pi, pj) <= tolerance)
|
||||
{
|
||||
foreach (var list in indexLists)
|
||||
list.Update(i, j);
|
||||
mesh.Positions.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge vertices with the same <see cref="VertexContent.PositionIndices"/> and
|
||||
/// <see cref="VertexChannel"/> data within the specified
|
||||
/// <see cref="GeometryContent"/>.
|
||||
/// </summary>
|
||||
/// <param name="geometry">Geometry to be processed.</param>
|
||||
public static void MergeDuplicateVertices(GeometryContent geometry)
|
||||
{
|
||||
if (geometry == null)
|
||||
throw new ArgumentNullException("geometry");
|
||||
|
||||
var verts = geometry.Vertices;
|
||||
var hashMap = new Dictionary<int, List<VertexData>>();
|
||||
|
||||
var indices = new IndexUpdateList(geometry.Indices);
|
||||
var vIndex = 0;
|
||||
|
||||
for (var i = 0; i < geometry.Indices.Count; i++)
|
||||
{
|
||||
var iIndex = geometry.Indices[i];
|
||||
var iData = new VertexData
|
||||
{
|
||||
Index = iIndex,
|
||||
PositionIndex = verts.PositionIndices[vIndex],
|
||||
ChannelData = new object[verts.Channels.Count]
|
||||
};
|
||||
|
||||
for (var channel = 0; channel < verts.Channels.Count; channel++)
|
||||
iData.ChannelData[channel] = verts.Channels[channel][vIndex];
|
||||
|
||||
var hash = iData.ComputeHash();
|
||||
|
||||
var merged = false;
|
||||
List<VertexData> candidates;
|
||||
if (hashMap.TryGetValue(hash, out candidates))
|
||||
{
|
||||
for (var candidateIndex = 0; candidateIndex < candidates.Count; candidateIndex++)
|
||||
{
|
||||
var c = candidates[candidateIndex];
|
||||
if (!iData.ContentEquals(c))
|
||||
continue;
|
||||
|
||||
// Match! Update the corresponding indices and remove the vertex
|
||||
indices.Update(iIndex, c.Index);
|
||||
verts.RemoveAt(vIndex);
|
||||
merged = true;
|
||||
}
|
||||
if (!merged)
|
||||
candidates.Add(iData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no vertices with the same hash yet, create a new list for the data
|
||||
hashMap.Add(hash, new List<VertexData> { iData });
|
||||
}
|
||||
|
||||
if (!merged)
|
||||
vIndex++;
|
||||
}
|
||||
|
||||
// update the indices because of the vertices we removed
|
||||
indices.Pack();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge vertices with the same <see cref="VertexContent.PositionIndices"/> and
|
||||
/// <see cref="VertexChannel"/> data within the <see cref="MeshContent.Geometry"/>
|
||||
/// of this mesh. If you want to merge positions too, call
|
||||
/// <see cref="MergeDuplicatePositions"/> on your mesh before this function.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to be processed</param>
|
||||
public static void MergeDuplicateVertices(MeshContent mesh)
|
||||
{
|
||||
if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
foreach (var geom in mesh.Geometry)
|
||||
MergeDuplicateVertices(geom);
|
||||
}
|
||||
|
||||
public static void OptimizeForCache(MeshContent mesh)
|
||||
{
|
||||
// We don't throw here as non-optimized still works.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the triangle winding order of the mesh.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh which will be modified.</param>
|
||||
/// <remarks>
|
||||
/// This method is useful when changing the direction of backface culling
|
||||
/// like when switching between left/right handed coordinate systems.
|
||||
/// </remarks>
|
||||
public static void SwapWindingOrder(MeshContent mesh)
|
||||
{
|
||||
// Gotta have a mesh to run!
|
||||
if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
foreach (var geom in mesh.Geometry)
|
||||
{
|
||||
for (var i = 0; i < geom.Indices.Count; i += 3)
|
||||
{
|
||||
var first = geom.Indices[i];
|
||||
var last = geom.Indices[i+2];
|
||||
geom.Indices[i] = last;
|
||||
geom.Indices[i+2] = first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the contents of a node and its descendants.
|
||||
/// </summary>
|
||||
/// <remarks>The node transforms themselves are unaffected.</remarks>
|
||||
/// <param name="scene">The root node of the scene to transform.</param>
|
||||
/// <param name="transform">The transform matrix to apply to the scene.</param>
|
||||
public static void TransformScene(NodeContent scene, Matrix transform)
|
||||
{
|
||||
if (scene == null)
|
||||
throw new ArgumentException("scene");
|
||||
|
||||
// If the transformation is an identity matrix, this is a no-op and
|
||||
// we can save ourselves a bunch of work in the first place.
|
||||
if (transform == Matrix.Identity)
|
||||
return;
|
||||
|
||||
var inverseTransform = Matrix.Invert(transform);
|
||||
|
||||
var work = new Stack<NodeContent>();
|
||||
work.Push(scene);
|
||||
|
||||
while (work.Count > 0)
|
||||
{
|
||||
var node = work.Pop();
|
||||
foreach (var child in node.Children)
|
||||
work.Push(child);
|
||||
|
||||
// Transform the mesh content.
|
||||
var mesh = node as MeshContent;
|
||||
if (mesh != null)
|
||||
mesh.TransformContents(ref transform);
|
||||
|
||||
// Transform local coordinate system using "similarity transform".
|
||||
node.Transform = inverseTransform * node.Transform * transform;
|
||||
|
||||
// Transform animations.
|
||||
foreach (var animationContent in node.Animations.Values)
|
||||
foreach (var animationChannel in animationContent.Channels.Values)
|
||||
for (int i = 0; i < animationChannel.Count; i++)
|
||||
animationChannel[i].Transform = inverseTransform * animationChannel[i].Transform * transform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified transform is left-handed.
|
||||
/// </summary>
|
||||
/// <param name="xform">The transform.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if <paramref name="xform"/> is left-handed; otherwise,
|
||||
/// <see langword="false"/> if <paramref name="xform"/> is right-handed.
|
||||
/// </returns>
|
||||
internal static bool IsLeftHanded(ref Matrix xform)
|
||||
{
|
||||
// Check sign of determinant of upper-left 3x3 matrix:
|
||||
// positive determinant ... right-handed
|
||||
// negative determinant ... left-handed
|
||||
|
||||
// Since XNA does not have a 3x3 matrix, use the "scalar triple product"
|
||||
// (see http://en.wikipedia.org/wiki/Triple_product) to calculate the
|
||||
// determinant.
|
||||
float d = Vector3.Dot(xform.Right, Vector3.Cross(xform.Forward, xform.Up));
|
||||
return d < 0.0f;
|
||||
}
|
||||
|
||||
#region Private helpers
|
||||
|
||||
private static void UpdatePositionIndices(MeshContent mesh, int from, int to)
|
||||
{
|
||||
foreach (var geom in mesh.Geometry)
|
||||
{
|
||||
for (var i = 0; i < geom.Vertices.PositionIndices.Count; i++)
|
||||
{
|
||||
var index = geom.Vertices.PositionIndices[i];
|
||||
if (index == from)
|
||||
geom.Vertices.PositionIndices[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class VertexData
|
||||
{
|
||||
public int Index;
|
||||
public int PositionIndex;
|
||||
public object[] ChannelData;
|
||||
|
||||
// Compute a hash based on PositionIndex and ChannelData
|
||||
public int ComputeHash()
|
||||
{
|
||||
var hash = PositionIndex;
|
||||
foreach (var channel in ChannelData)
|
||||
hash ^= channel.GetHashCode();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Check equality on PositionIndex and ChannelData
|
||||
public bool ContentEquals(VertexData other)
|
||||
{
|
||||
if (PositionIndex != other.PositionIndex)
|
||||
return false;
|
||||
|
||||
if (ChannelData.Length != other.ChannelData.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < ChannelData.Length; i++)
|
||||
{
|
||||
if (!Equals(ChannelData[i], other.ChannelData[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// takes an IndexCollection and can efficiently update index values
|
||||
private class IndexUpdateList
|
||||
{
|
||||
private readonly IList<int> _collectionToUpdate;
|
||||
private readonly Dictionary<int, List<int>> _indexPositions;
|
||||
|
||||
// create the list, presort the values and compute the start positions of each value
|
||||
public IndexUpdateList(IList<int> collectionToUpdate)
|
||||
{
|
||||
_collectionToUpdate = collectionToUpdate;
|
||||
_indexPositions = new Dictionary<int, List<int>>();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
for (var pos = 0; pos < _collectionToUpdate.Count; pos++)
|
||||
{
|
||||
var v = _collectionToUpdate[pos];
|
||||
if (_indexPositions.ContainsKey(v))
|
||||
_indexPositions[v].Add(pos);
|
||||
else
|
||||
_indexPositions.Add(v, new List<int> {pos});
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(int from, int to)
|
||||
{
|
||||
if (from == to || !_indexPositions.ContainsKey(from))
|
||||
return;
|
||||
|
||||
foreach (var pos in _indexPositions[from])
|
||||
_collectionToUpdate[pos] = to;
|
||||
|
||||
if (_indexPositions.ContainsKey(to))
|
||||
_indexPositions[to].AddRange(_indexPositions[from]);
|
||||
else
|
||||
_indexPositions.Add(to, _indexPositions[from]);
|
||||
|
||||
_indexPositions.Remove(from);
|
||||
}
|
||||
|
||||
// Pack all indices together starting from zero
|
||||
// E.g. [5, 5, 3, 5, 21, 3] -> [1, 1, 0, 1, 2, 0]
|
||||
// note that the order must be kept
|
||||
public void Pack()
|
||||
{
|
||||
if (_collectionToUpdate.Count == 0)
|
||||
return;
|
||||
|
||||
var sorted = new SortedSet<int>(_collectionToUpdate);
|
||||
|
||||
var newIndex = 0;
|
||||
foreach (var value in sorted)
|
||||
{
|
||||
foreach (var pos in _indexPositions[value])
|
||||
_collectionToUpdate[pos] = newIndex;
|
||||
|
||||
newIndex++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for accessing a mipmap chain.
|
||||
/// </summary>
|
||||
public sealed class MipmapChain : Collection<BitmapContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of MipmapChain.
|
||||
/// </summary>
|
||||
public MipmapChain()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of MipmapChain with the specified mipmap.
|
||||
/// </summary>
|
||||
/// <param name="bitmap"></param>
|
||||
public MipmapChain(BitmapContent bitmap)
|
||||
{
|
||||
Add(bitmap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new mipmap chain containing the specified bitmap.
|
||||
/// </summary>
|
||||
/// <param name="bitmap">Bitmap used for the mipmap chain.</param>
|
||||
/// <returns>Resultant mipmap chain.</returns>
|
||||
public static implicit operator MipmapChain(BitmapContent bitmap)
|
||||
{
|
||||
return new MipmapChain(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// 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.ObjectModel;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for maintaining a mipmap chain.
|
||||
/// </summary>
|
||||
public sealed class MipmapChainCollection : Collection<MipmapChain>
|
||||
{
|
||||
private readonly bool _fixedSize;
|
||||
|
||||
private const string CannotResizeError = "Cannot resize MipmapChainCollection. This type of texture has a fixed number of faces.";
|
||||
|
||||
internal MipmapChainCollection(int count, bool fixedSize)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
Add(new MipmapChain());
|
||||
|
||||
_fixedSize = fixedSize;
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
if (_fixedSize)
|
||||
throw new NotSupportedException(CannotResizeError);
|
||||
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
if (_fixedSize)
|
||||
throw new NotSupportedException(CannotResizeError);
|
||||
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, MipmapChain item)
|
||||
{
|
||||
if (_fixedSize)
|
||||
throw new NotSupportedException(CannotResizeError);
|
||||
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for graphics types that define local coordinate systems.
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerDisplay("Node '{Name}'")]
|
||||
public class NodeContent : ContentItem
|
||||
{
|
||||
Matrix transform;
|
||||
NodeContent parent;
|
||||
NodeContentCollection children;
|
||||
AnimationContentDictionary animations;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the local Transform property, multiplied by the AbsoluteTransform of the parent.
|
||||
/// </summary>
|
||||
public Matrix AbsoluteTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (parent != null)
|
||||
return transform * parent.AbsoluteTransform;
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of animations belonging to this node.
|
||||
/// </summary>
|
||||
public AnimationContentDictionary Animations
|
||||
{
|
||||
get
|
||||
{
|
||||
return animations;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the NodeContent object.
|
||||
/// </summary>
|
||||
public NodeContentCollection Children
|
||||
{
|
||||
get
|
||||
{
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this NodeContent object.
|
||||
/// </summary>
|
||||
public NodeContent Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
set
|
||||
{
|
||||
parent = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform matrix of the scene.
|
||||
/// The transform matrix defines a local coordinate system for the content in addition to any children of this object.
|
||||
/// </summary>
|
||||
public Matrix Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
set
|
||||
{
|
||||
transform = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of NodeContent.
|
||||
/// </summary>
|
||||
public NodeContent()
|
||||
{
|
||||
children = new NodeContentCollection(this);
|
||||
animations = new AnimationContentDictionary();
|
||||
Transform = Matrix.Identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class NodeContentCollection : ChildCollection<NodeContent, NodeContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of NodeContentCollection.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent object of the child objects returned in the collection.</param>
|
||||
internal NodeContentCollection(NodeContent parent)
|
||||
: base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of a child object.
|
||||
/// </summary>
|
||||
/// <param name="child">The child of the parent being retrieved.</param>
|
||||
/// <returns>The parent of the child object.</returns>
|
||||
protected override NodeContent GetParent(NodeContent child)
|
||||
{
|
||||
return child.Parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the value of the parent object of the specified child object.
|
||||
/// </summary>
|
||||
/// <param name="child">The child of the parent being modified.</param>
|
||||
/// <param name="parent">The new value for the parent object.</param>
|
||||
protected override void SetParent(NodeContent child, NodeContent parent)
|
||||
{
|
||||
child.Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
// 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 Microsoft.Xna.Framework.Content.Pipeline.Utilities;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Graphics.PackedVector;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class PixelBitmapContent<T> : BitmapContent where T : struct, IEquatable<T>
|
||||
{
|
||||
internal T[][] _pixelData;
|
||||
|
||||
internal SurfaceFormat _format;
|
||||
|
||||
public PixelBitmapContent(int width, int height)
|
||||
{
|
||||
if (!TryGetFormat(out _format))
|
||||
throw new InvalidOperationException(string.Format("Color format \"{0}\" is not supported",typeof(T).ToString()));
|
||||
Height = height;
|
||||
Width = width;
|
||||
|
||||
_pixelData = new T[height][];
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
_pixelData[y] = new T[width];
|
||||
|
||||
}
|
||||
|
||||
public override byte[] GetPixelData()
|
||||
{
|
||||
var formatSize = _format.GetSize();
|
||||
var dataSize = Width * Height * formatSize;
|
||||
var outputData = new byte[dataSize];
|
||||
|
||||
for (var x = 0; x < Height; x++)
|
||||
{
|
||||
var dataHandle = GCHandle.Alloc(_pixelData[x], GCHandleType.Pinned);
|
||||
var dataPtr = (IntPtr)(dataHandle.AddrOfPinnedObject().ToInt64());
|
||||
|
||||
Marshal.Copy(dataPtr, outputData, (formatSize * x * Width), (Width * formatSize));
|
||||
|
||||
dataHandle.Free();
|
||||
}
|
||||
|
||||
return outputData;
|
||||
}
|
||||
|
||||
public override void SetPixelData(byte[] sourceData)
|
||||
{
|
||||
var size = _format.GetSize();
|
||||
|
||||
for (var x = 0; x < Height; x++)
|
||||
{
|
||||
var dataHandle = GCHandle.Alloc(_pixelData[x], GCHandleType.Pinned);
|
||||
var dataPtr = (IntPtr)dataHandle.AddrOfPinnedObject().ToInt64();
|
||||
|
||||
Marshal.Copy(sourceData, (x * Width * size), dataPtr, Width * size);
|
||||
|
||||
dataHandle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public T[] GetRow(int y)
|
||||
{
|
||||
if (y < 0 || y >= Height)
|
||||
throw new ArgumentOutOfRangeException("y");
|
||||
|
||||
return _pixelData[y];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
if (typeof(T) == typeof(Color))
|
||||
format = SurfaceFormat.Color;
|
||||
else if (typeof(T) == typeof(Bgra4444))
|
||||
format = SurfaceFormat.Bgra4444;
|
||||
else if (typeof(T) == typeof(Bgra5551))
|
||||
format = SurfaceFormat.Bgra5551;
|
||||
else if (typeof(T) == typeof(Bgr565))
|
||||
format = SurfaceFormat.Bgr565;
|
||||
else if (typeof(T) == typeof(Vector4))
|
||||
format = SurfaceFormat.Vector4;
|
||||
else if (typeof(T) == typeof(Vector2))
|
||||
format = SurfaceFormat.Vector2;
|
||||
else if (typeof(T) == typeof(Single))
|
||||
format = SurfaceFormat.Single;
|
||||
else if (typeof(T) == typeof(byte))
|
||||
format = SurfaceFormat.Alpha8;
|
||||
else if (typeof(T) == typeof(Rgba64))
|
||||
format = SurfaceFormat.Rgba64;
|
||||
else if (typeof(T) == typeof(Rgba1010102))
|
||||
format = SurfaceFormat.Rgba1010102;
|
||||
else if (typeof(T) == typeof(Rg32))
|
||||
format = SurfaceFormat.Rg32;
|
||||
else if (typeof(T) == typeof(Byte4))
|
||||
format = SurfaceFormat.Color;
|
||||
else if (typeof(T) == typeof(NormalizedByte2))
|
||||
format = SurfaceFormat.NormalizedByte2;
|
||||
else if (typeof(T) == typeof(NormalizedByte4))
|
||||
format = SurfaceFormat.NormalizedByte4;
|
||||
else if (typeof(T) == typeof(HalfSingle))
|
||||
format = SurfaceFormat.HalfSingle;
|
||||
else if (typeof(T) == typeof(HalfVector2))
|
||||
format = SurfaceFormat.HalfVector2;
|
||||
else if (typeof(T) == typeof(HalfVector4))
|
||||
format = SurfaceFormat.HalfVector4;
|
||||
else
|
||||
{
|
||||
format = SurfaceFormat.Color;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public T GetPixel(int x, int y)
|
||||
{
|
||||
return _pixelData[y][x];
|
||||
}
|
||||
|
||||
public void SetPixel(int x, int y, T value)
|
||||
{
|
||||
_pixelData[y][x] = value;
|
||||
}
|
||||
|
||||
public void ReplaceColor(T originalColor, T newColor)
|
||||
{
|
||||
for (var y = 0; y < Height; y++)
|
||||
{
|
||||
for (var x = 0; x < Width; x++)
|
||||
{
|
||||
if (_pixelData[y][x].Equals(originalColor))
|
||||
_pixelData[y][x] = newColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
return false;
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (_format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
SetPixelData(sourceBitmap.GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from a Vector4 format
|
||||
var src = sourceBitmap as PixelBitmapContent<Vector4>;
|
||||
if (default(T) is IPackedVector)
|
||||
{
|
||||
Parallel.For(0, sourceRegion.Height, (y) =>
|
||||
{
|
||||
var pixel = default(T);
|
||||
var p = (IPackedVector)pixel;
|
||||
var row = src.GetRow(sourceRegion.Top + y);
|
||||
for (int x = 0; x < sourceRegion.Width; ++x)
|
||||
{
|
||||
p.PackFromVector4(row[sourceRegion.Left + x]);
|
||||
pixel = (T)p;
|
||||
SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, pixel);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var converter = new Vector4Converter() as IVector4Converter<T>;
|
||||
// If no converter could be created, converting from this format is not supported
|
||||
if (converter == null)
|
||||
return false;
|
||||
|
||||
Parallel.For(0, sourceRegion.Height, (y) =>
|
||||
{
|
||||
var row = src.GetRow(sourceRegion.Top + y);
|
||||
for (int x = 0; x < sourceRegion.Width; ++x)
|
||||
{
|
||||
SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, converter.FromVector4(row[sourceRegion.Left + x]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
return false;
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (_format == destinationFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
destinationBitmap.SetPixelData(GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the destination is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(destinationBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(this, sourceRegion, destinationBitmap, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to a Vector4 format
|
||||
var dest = destinationBitmap as PixelBitmapContent<Vector4>;
|
||||
if (default(T) is IPackedVector)
|
||||
{
|
||||
Parallel.For(0, sourceRegion.Height, (y) =>
|
||||
{
|
||||
var row = GetRow(sourceRegion.Top + y);
|
||||
for (int x = 0; x < sourceRegion.Width; ++x)
|
||||
dest.SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, ((IPackedVector)row[sourceRegion.Left + x]).ToVector4());
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var converter = new Vector4Converter() as IVector4Converter<T>;
|
||||
// If no converter could be created, converting from this format is not supported
|
||||
if (converter == null)
|
||||
return false;
|
||||
|
||||
Parallel.For(0, sourceRegion.Height, (y) =>
|
||||
{
|
||||
var row = GetRow(sourceRegion.Top + y);
|
||||
for (int x = 0; x < sourceRegion.Width; ++x)
|
||||
dest.SetPixel(destinationRegion.Left + x, destinationRegion.Top + y, converter.ToVector4(row[sourceRegion.Left + x]));
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a collection of vertex position values.
|
||||
/// </summary>
|
||||
public sealed class PositionCollection : Collection<Vector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of PositionCollection.
|
||||
/// </summary>
|
||||
public PositionCollection()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
using PVRTexLibNET;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public abstract class PvrtcBitmapContent : BitmapContent
|
||||
{
|
||||
internal byte[] _bitmapData;
|
||||
|
||||
public PvrtcBitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
int GetDataSize()
|
||||
{
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat.RgbPvrtc2Bpp:
|
||||
case SurfaceFormat.RgbaPvrtc2Bpp:
|
||||
return (Math.Max(Width, 16) * Math.Max(Height, 8) * 2 + 7) / 8;
|
||||
|
||||
case SurfaceFormat.RgbPvrtc4Bpp:
|
||||
case SurfaceFormat.RgbaPvrtc4Bpp:
|
||||
return (Math.Max(Width, 8) * Math.Max(Height, 8) * 4 + 7) / 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override byte[] GetPixelData()
|
||||
{
|
||||
if (_bitmapData == null)
|
||||
throw new InvalidOperationException("No data set on bitmap");
|
||||
var result = new byte[_bitmapData.Length];
|
||||
Buffer.BlockCopy(_bitmapData, 0, result, 0, _bitmapData.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void SetPixelData(byte[] sourceData)
|
||||
{
|
||||
var size = GetDataSize();
|
||||
if (sourceData.Length != size)
|
||||
throw new ArgumentException("Incorrect data size. Expected " + size + " bytes");
|
||||
if (_bitmapData == null || _bitmapData.Length != size)
|
||||
_bitmapData = new byte[size];
|
||||
Buffer.BlockCopy(sourceData, 0, _bitmapData, 0, size);
|
||||
}
|
||||
|
||||
protected override bool TryCopyFrom(BitmapContent sourceBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat sourceFormat;
|
||||
if (!sourceBitmap.TryGetFormat(out sourceFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (format == sourceFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
SetPixelData(sourceBitmap.GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Destination region copy is not yet supported
|
||||
if (destinationRegion != new Rectangle(0, 0, Width, Height))
|
||||
return false;
|
||||
|
||||
// If the source is not Vector4 or requires resizing, send it through BitmapContent.Copy
|
||||
if (!(sourceBitmap is PixelBitmapContent<Vector4>) || sourceRegion.Width != destinationRegion.Width || sourceRegion.Height != destinationRegion.Height)
|
||||
{
|
||||
try
|
||||
{
|
||||
BitmapContent.Copy(sourceBitmap, sourceRegion, this, destinationRegion);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat targetFormat;
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat.RgbPvrtc2Bpp:
|
||||
targetFormat = PixelFormat.PVRTCI_2bpp_RGB;
|
||||
break;
|
||||
case SurfaceFormat.RgbaPvrtc2Bpp:
|
||||
targetFormat = PixelFormat.PVRTCI_2bpp_RGBA;
|
||||
break;
|
||||
case SurfaceFormat.RgbPvrtc4Bpp:
|
||||
targetFormat = PixelFormat.PVRTCI_4bpp_RGB;
|
||||
break;
|
||||
case SurfaceFormat.RgbaPvrtc4Bpp:
|
||||
targetFormat = PixelFormat.PVRTCI_4bpp_RGBA;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the texture object in the PVR library
|
||||
var sourceData = sourceBitmap.GetPixelData();
|
||||
var rgba32F = (PixelFormat)0x2020202061626772; // static const PixelType PVRStandard32PixelType = PixelType('r', 'g', 'b', 'a', 32, 32, 32, 32);
|
||||
using (var pvrTexture = PVRTexture.CreateTexture(sourceData, (uint)sourceBitmap.Width, (uint)sourceBitmap.Height, 1,
|
||||
rgba32F, true, VariableType.Float, ColourSpace.lRGB))
|
||||
{
|
||||
// Resize the bitmap if needed
|
||||
if ((sourceBitmap.Width != Width) || (sourceBitmap.Height != Height))
|
||||
pvrTexture.Resize((uint)Width, (uint)Height, 1, ResizeMode.Cubic);
|
||||
// On Linux, anything less than CompressorQuality.PVRTCHigh crashes in libpthread.so at the end of compression
|
||||
pvrTexture.Transcode(targetFormat, VariableType.UnsignedByte, ColourSpace.lRGB, CompressorQuality.PVRTCHigh);
|
||||
var texDataSize = pvrTexture.GetTextureDataSize(0);
|
||||
var texData = new byte[texDataSize];
|
||||
pvrTexture.GetTextureData(texData, texDataSize);
|
||||
SetPixelData(texData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCopyTo(BitmapContent destinationBitmap, Rectangle sourceRegion, Rectangle destinationRegion)
|
||||
{
|
||||
SurfaceFormat destinationFormat;
|
||||
if (!destinationBitmap.TryGetFormat(out destinationFormat))
|
||||
return false;
|
||||
|
||||
SurfaceFormat format;
|
||||
TryGetFormat(out format);
|
||||
|
||||
// A shortcut for copying the entire bitmap to another bitmap of the same type and format
|
||||
if (format == destinationFormat && (sourceRegion == new Rectangle(0, 0, Width, Height)) && sourceRegion == destinationRegion)
|
||||
{
|
||||
destinationBitmap.SetPixelData(GetPixelData());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other support for copying from a PVR texture yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class PvrtcRgb2BitmapContent : PvrtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of PvrtcRgb2BitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public PvrtcRgb2BitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbPvrtc2Bpp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "PVRTC RGB 2bpp " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class PvrtcRgb4BitmapContent : PvrtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of PvrtcRgb4BitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public PvrtcRgb4BitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbPvrtc4Bpp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "PVRTC RGB 4bpp " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class PvrtcRgba2BitmapContent : PvrtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of PvrtcRgba2BitBitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public PvrtcRgba2BitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbaPvrtc2Bpp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "PVRTC RGBA 2bpp " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class PvrtcRgba4BitmapContent : PvrtcBitmapContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of PvrtcRgba4BitBitmapContent with the specified width and height.
|
||||
/// </summary>
|
||||
/// <param name="width">The width in pixels of the bitmap.</param>
|
||||
/// <param name="height">The height in pixels of the bitmap.</param>
|
||||
public PvrtcRgba4BitmapContent(int width, int height)
|
||||
: base(width, height)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding GPU texture format for the specified bitmap type.
|
||||
/// </summary>
|
||||
/// <param name="format">Format being retrieved.</param>
|
||||
/// <returns>The GPU texture format of the bitmap type.</returns>
|
||||
public override bool TryGetFormat(out SurfaceFormat format)
|
||||
{
|
||||
format = SurfaceFormat.RgbaPvrtc4Bpp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string description of the bitmap.
|
||||
/// </summary>
|
||||
/// <returns>Description of the bitmap.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "PVRTC RGBA 4bpp " + Width + "x" + Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class SkinnedMaterialContent : MaterialContent
|
||||
{
|
||||
public const string AlphaKey = "Alpha";
|
||||
public const string DiffuseColorKey = "DiffuseColor";
|
||||
public const string EmissiveColorKey = "EmissiveColor";
|
||||
public const string SpecularColorKey = "SpecularColor";
|
||||
public const string SpecularPowerKey = "SpecularPower";
|
||||
public const string TextureKey = "Texture";
|
||||
public const string WeightsPerVertexKey = "WeightsPerVertex";
|
||||
|
||||
public float? Alpha
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(AlphaKey); }
|
||||
set { SetProperty(AlphaKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? DiffuseColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(DiffuseColorKey); }
|
||||
set { SetProperty(DiffuseColorKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? EmissiveColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(EmissiveColorKey); }
|
||||
set { SetProperty(EmissiveColorKey, value); }
|
||||
}
|
||||
|
||||
public Vector3? SpecularColor
|
||||
{
|
||||
get { return GetValueTypeProperty<Vector3>(SpecularColorKey); }
|
||||
set { SetProperty(SpecularColorKey, value); }
|
||||
}
|
||||
|
||||
public float? SpecularPower
|
||||
{
|
||||
get { return GetValueTypeProperty<float>(SpecularPowerKey); }
|
||||
set { SetProperty(SpecularPowerKey, value); }
|
||||
}
|
||||
|
||||
public ExternalReference<TextureContent> Texture
|
||||
{
|
||||
get { return GetTexture(TextureKey); }
|
||||
set { SetTexture(TextureKey, value); }
|
||||
}
|
||||
|
||||
public int? WeightsPerVertex
|
||||
{
|
||||
get { return GetValueTypeProperty<int>(WeightsPerVertexKey); }
|
||||
set { SetProperty(WeightsPerVertexKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for all texture objects.
|
||||
/// </summary>
|
||||
public abstract class SpriteFontDescriptionContent : ContentItem, IDisposable
|
||||
{
|
||||
MipmapChainCollection faces;
|
||||
internal Bitmap _bitmap;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of image faces that hold a single mipmap chain for a regular 2D texture, six chains for a cube map, or an arbitrary number for volume and array textures.
|
||||
/// </summary>
|
||||
public MipmapChainCollection Faces
|
||||
{
|
||||
get
|
||||
{
|
||||
return faces;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of TextureContent with the specified face collection.
|
||||
/// </summary>
|
||||
/// <param name="faces">Mipmap chain containing the face collection.</param>
|
||||
protected TextureContent(MipmapChainCollection faces)
|
||||
{
|
||||
this.faces = faces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts all bitmaps for this texture to a different format.
|
||||
/// </summary>
|
||||
/// <param name="newBitmapType">Type being converted to. The new type must be a subclass of BitmapContent, such as PixelBitmapContent or DxtBitmapContent.</param>
|
||||
public void ConvertBitmapType(Type newBitmapType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a full set of mipmaps for the texture.
|
||||
/// </summary>
|
||||
/// <param name="overwriteExistingMipmaps">true if the existing mipmap set is replaced with the new set; false otherwise.</param>
|
||||
public virtual void GenerateMipmaps(bool overwriteExistingMipmaps)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that all contents of this texture are present, correct and match the capabilities of the device.
|
||||
/// </summary>
|
||||
/// <param name="targetProfile">The profile identifier that defines the capabilities of the device.</param>
|
||||
public abstract void Validate(Nullable<GraphicsProfile> targetProfile);
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (_bitmap != null)
|
||||
{
|
||||
_bitmap.Dispose();
|
||||
_bitmap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class Texture2DContent : TextureContent
|
||||
{
|
||||
public MipmapChain Mipmaps
|
||||
{
|
||||
get { return Faces[0]; }
|
||||
set { Faces[0] = value; }
|
||||
}
|
||||
|
||||
public Texture2DContent() :
|
||||
base(new MipmapChainCollection(1, true))
|
||||
{
|
||||
}
|
||||
|
||||
public override void Validate(GraphicsProfile? targetProf)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class Texture3DContent : TextureContent
|
||||
{
|
||||
public Texture3DContent() :
|
||||
base(new MipmapChainCollection(0, false))
|
||||
{
|
||||
}
|
||||
|
||||
public override void Validate(GraphicsProfile? targetProf)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GenerateMipmaps(bool overwriteExistingMipmaps)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
// 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.Linq;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for all texture objects.
|
||||
/// </summary>
|
||||
public abstract class TextureContent : ContentItem
|
||||
{
|
||||
MipmapChainCollection faces;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of image faces that hold a single mipmap chain for a regular 2D texture, six chains for a cube map, or an arbitrary number for volume and array textures.
|
||||
/// </summary>
|
||||
public MipmapChainCollection Faces
|
||||
{
|
||||
get
|
||||
{
|
||||
return faces;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of TextureContent with the specified face collection.
|
||||
/// </summary>
|
||||
/// <param name="faces">Mipmap chain containing the face collection.</param>
|
||||
protected TextureContent(MipmapChainCollection faces)
|
||||
{
|
||||
this.faces = faces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts all bitmaps for this texture to a different format.
|
||||
/// </summary>
|
||||
/// <param name="newBitmapType">Type being converted to. The new type must be a subclass of BitmapContent, such as PixelBitmapContent or DxtBitmapContent.</param>
|
||||
public void ConvertBitmapType(Type newBitmapType)
|
||||
{
|
||||
if (newBitmapType == null)
|
||||
throw new ArgumentNullException("newBitmapType");
|
||||
|
||||
if (!newBitmapType.IsSubclassOf(typeof (BitmapContent)))
|
||||
throw new ArgumentException(string.Format("Type '{0}' is not a subclass of BitmapContent.", newBitmapType));
|
||||
|
||||
if (newBitmapType.IsAbstract)
|
||||
throw new ArgumentException(string.Format("Type '{0}' is abstract and cannot be allocated.", newBitmapType));
|
||||
|
||||
if (newBitmapType.ContainsGenericParameters)
|
||||
throw new ArgumentException(string.Format("Type '{0}' contains generic parameters and cannot be allocated.", newBitmapType));
|
||||
|
||||
if (newBitmapType.GetConstructor(new Type[2] {typeof (int), typeof (int)}) == null)
|
||||
throw new ArgumentException(string.Format("Type '{0} does not have a constructor with signature (int, int) and cannot be allocated.",
|
||||
newBitmapType));
|
||||
|
||||
foreach (var mipChain in faces)
|
||||
{
|
||||
for (var i = 0; i < mipChain.Count; i++)
|
||||
{
|
||||
var src = mipChain[i];
|
||||
if (src.GetType() != newBitmapType)
|
||||
{
|
||||
var dst = (BitmapContent)Activator.CreateInstance(newBitmapType, new object[] { src.Width,src.Height });
|
||||
BitmapContent.Copy(src, dst);
|
||||
mipChain[i] = dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a full set of mipmaps for the texture.
|
||||
/// </summary>
|
||||
/// <param name="overwriteExistingMipmaps">true if the existing mipmap set is replaced with the new set; false otherwise.</param>
|
||||
public virtual void GenerateMipmaps(bool overwriteExistingMipmaps)
|
||||
{
|
||||
// If we already have mipmaps and we're not supposed to overwrite
|
||||
// them then return without any generation.
|
||||
if (!overwriteExistingMipmaps && faces.Any(f => f.Count > 1))
|
||||
return;
|
||||
|
||||
// Generate the mips for each face.
|
||||
foreach (var face in faces)
|
||||
{
|
||||
// Remove any existing mipmaps.
|
||||
var faceBitmap = face[0];
|
||||
face.Clear();
|
||||
face.Add(faceBitmap);
|
||||
var faceType = faceBitmap.GetType();
|
||||
int width = faceBitmap.Width;
|
||||
int height = faceBitmap.Height;
|
||||
while (width > 1 || height > 1)
|
||||
{
|
||||
if (width > 1)
|
||||
width /= 2;
|
||||
if (height > 1)
|
||||
height /= 2;
|
||||
|
||||
var mip = (BitmapContent)Activator.CreateInstance(faceType, new object[] { width, height });
|
||||
BitmapContent.Copy(faceBitmap, mip);
|
||||
face.Add(mip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that all contents of this texture are present, correct and match the capabilities of the device.
|
||||
/// </summary>
|
||||
/// <param name="targetProfile">The profile identifier that defines the capabilities of the device.</param>
|
||||
public abstract void Validate(GraphicsProfile? targetProfile);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// 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 Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class TextureCubeContent : TextureContent
|
||||
{
|
||||
public TextureCubeContent() :
|
||||
base(new MipmapChainCollection(6, true))
|
||||
{
|
||||
}
|
||||
|
||||
public override void Validate(GraphicsProfile? targetProf)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// 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 Microsoft.Xna.Framework.Content.Pipeline.Processors;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public abstract class TextureProfile
|
||||
{
|
||||
private static readonly LoadedTypeCollection<TextureProfile> _profiles = new LoadedTypeCollection<TextureProfile>();
|
||||
|
||||
/// <summary>
|
||||
/// Find the profile for this target platform.
|
||||
/// </summary>
|
||||
/// <param name="platform">The platform target for textures.</param>
|
||||
/// <returns></returns>
|
||||
public static TextureProfile ForPlatform(TargetPlatform platform)
|
||||
{
|
||||
var profile = _profiles.FirstOrDefault(h => h.Supports(platform));
|
||||
if (profile != null)
|
||||
return profile;
|
||||
|
||||
throw new PipelineException("There is no supported texture profile for the '" + platform + "' platform!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this profile supports texture processing for this platform.
|
||||
/// </summary>
|
||||
public abstract bool Supports(TargetPlatform platform);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the texture format will require power-of-two dimensions and/or equal width and height.
|
||||
/// </summary>
|
||||
/// <param name="context">The processor context.</param>
|
||||
/// <param name="format">The desired texture format.</param>
|
||||
/// <param name="requiresPowerOfTwo">True if the texture format requires power-of-two dimensions.</param>
|
||||
/// <param name="requiresSquare">True if the texture format requires equal width and height.</param>
|
||||
/// <returns>True if the texture format requires power-of-two dimensions.</returns>
|
||||
public abstract void Requirements(ContentProcessorContext context, TextureProcessorOutputFormat format, out bool requiresPowerOfTwo, out bool requiresSquare);
|
||||
|
||||
/// <summary>
|
||||
/// Performs conversion of the texture content to the correct format.
|
||||
/// </summary>
|
||||
/// <param name="context">The processor context.</param>
|
||||
/// <param name="content">The content to be compressed.</param>
|
||||
/// <param name="format">The user requested format for compression.</param>
|
||||
/// <param name="isSpriteFont">If the texture has represents a sprite font, i.e. is greyscale and has sharp black/white contrast.</param>
|
||||
public void ConvertTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont)
|
||||
{
|
||||
// We do nothing in this case.
|
||||
if (format == TextureProcessorOutputFormat.NoChange)
|
||||
return;
|
||||
|
||||
// If this is color just make sure the format is right and return it.
|
||||
if (format == TextureProcessorOutputFormat.Color)
|
||||
{
|
||||
content.ConvertBitmapType(typeof(PixelBitmapContent<Color>));
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle this common compression format.
|
||||
if (format == TextureProcessorOutputFormat.Color16Bit)
|
||||
{
|
||||
GraphicsUtil.CompressColor16Bit(context, content);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// All other formats require platform specific choices.
|
||||
PlatformCompressTexture(context, content, format, isSpriteFont);
|
||||
}
|
||||
catch (EntryPointNotFoundException ex)
|
||||
{
|
||||
context.Logger.LogImportantMessage("Could not find the entry point to compress the texture. " + ex.ToString());
|
||||
throw ex;
|
||||
}
|
||||
catch (DllNotFoundException ex)
|
||||
{
|
||||
context.Logger.LogImportantMessage("Could not compress texture. Required shared lib is missing. " + ex.ToString());
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.Logger.LogImportantMessage("Could not convert texture. " + ex.ToString());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void PlatformCompressTexture(ContentProcessorContext context, TextureContent content, TextureProcessorOutputFormat format, bool isSpriteFont);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a collection of named references to texture files.
|
||||
/// </summary>
|
||||
public sealed class TextureReferenceDictionary : NamedValueDictionary<ExternalReference<TextureContent>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of TextureReferenceDictionary.
|
||||
/// </summary>
|
||||
public TextureReferenceDictionary()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for maintaining a vertex channel.
|
||||
/// A vertex channel is a list of arbitrary data with one value for each vertex. Channels are stored inside a GeometryContent and identified by name.
|
||||
/// </summary>
|
||||
public abstract class VertexChannel : IList, ICollection, IEnumerable
|
||||
{
|
||||
string name;
|
||||
|
||||
/// <summary>
|
||||
/// Allows overriding classes to implement the list, and for properties/methods in this class to access it.
|
||||
/// </summary>
|
||||
internal abstract IList Items
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the vertex channel
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return Items.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of data contained in this channel.
|
||||
/// </summary>
|
||||
public abstract Type ElementType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element at the specified index.
|
||||
/// </summary>
|
||||
public Object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Items[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
Items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the vertex channel.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether access to the collection is synchronized (thread safe).
|
||||
/// </summary>
|
||||
bool System.Collections.ICollection.IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can be used to synchronize access to the collection.
|
||||
/// </summary>
|
||||
Object System.Collections.ICollection.SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this list has a fixed size.
|
||||
/// </summary>
|
||||
bool System.Collections.IList.IsFixedSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this object is read-only.
|
||||
/// </summary>
|
||||
bool System.Collections.IList.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of VertexChannel.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the channel.</param>
|
||||
internal VertexChannel(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified element is in the channel.
|
||||
/// </summary>
|
||||
/// <param name="value">Element being searched for.</param>
|
||||
/// <returns>true if the element is present; false otherwise.</returns>
|
||||
public bool Contains(Object value)
|
||||
{
|
||||
return Items.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of the channel to an array, starting at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="array">Array that will receive the copied channel elements.</param>
|
||||
/// <param name="index">Starting index for copy operation.</param>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
((ICollection)Items).CopyTo(array, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator interface for reading channel content.
|
||||
/// </summary>
|
||||
/// <returns>Enumeration of the channel content.</returns>
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return Items.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified item.
|
||||
/// </summary>
|
||||
/// <param name="value">Item whose index is to be retrieved.</param>
|
||||
/// <returns>Index of specified item.</returns>
|
||||
public int IndexOf(Object value)
|
||||
{
|
||||
return Items.IndexOf(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads channel content and automatically converts it to the specified vector format.
|
||||
/// </summary>
|
||||
/// <typeparam name="TargetType">Target vector format of the converted data.</typeparam>
|
||||
/// <returns>The converted data.</returns>
|
||||
public abstract IEnumerable<TargetType> ReadConvertedContent<TargetType>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new element to the end of the collection.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to add.</param>
|
||||
/// <returns>Index of the element.</returns>
|
||||
int IList.Add(Object value)
|
||||
{
|
||||
return Items.Add(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all elements from the collection.
|
||||
/// </summary>
|
||||
void IList.Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an element into the collection at the specified position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index at which to insert the element.</param>
|
||||
/// <param name="value">The element to insert.</param>
|
||||
void IList.Insert(int index, Object value)
|
||||
{
|
||||
Items.Insert(index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the range of values from the enumerable into the channel.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the new elements should be inserted.</param>
|
||||
/// <param name="data">The data to insert into the channel.</param>
|
||||
internal abstract void InsertRange(int index, IEnumerable data);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a specified element from the collection.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to remove.</param>
|
||||
void IList.Remove(Object value)
|
||||
{
|
||||
Items.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the specified index position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the element to remove.</param>
|
||||
void IList.RemoveAt(int index)
|
||||
{
|
||||
Items.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of values from the channel.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based starting index of the range of elements to remove.</param>
|
||||
/// <param name="count"> The number of elements to remove.</param>
|
||||
internal abstract void RemoveRange(int index, int count);
|
||||
}
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for managing a list of vertex data channels.
|
||||
/// </summary>
|
||||
public sealed class VertexChannelCollection : IList<VertexChannel>, ICollection<VertexChannel>, IEnumerable<VertexChannel>, IEnumerable
|
||||
{
|
||||
List<VertexChannel> channels;
|
||||
VertexContent vertexContent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of vertex channels in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return channels.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex channel at the specified index position.
|
||||
/// </summary>
|
||||
public VertexChannel this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return channels[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
channels[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex channel with the specified name.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the collection is read-only.
|
||||
/// </summary>
|
||||
bool ICollection<VertexChannel>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of VertexChannelCollection.
|
||||
/// </summary>
|
||||
/// <param name="vertexContent">The VertexContent object that owns this collection.</param>
|
||||
internal VertexChannelCollection(VertexContent vertexContent)
|
||||
{
|
||||
this.vertexContent = vertexContent;
|
||||
channels = new List<VertexChannel>();
|
||||
_insertOverload = GetType().GetMethods().First(m => m.Name == "Insert" && m.IsGenericMethodDefinition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new vertex channel to the end of the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="ElementType">Type of the channel.</typeparam>
|
||||
/// <param name="name">Name of the new channel.</param>
|
||||
/// <param name="channelData">Initial data for the new channel. If null, the channel is filled with the default value for that type.</param>
|
||||
/// <returns>The newly added vertex channel.</returns>
|
||||
public VertexChannel<ElementType> Add<ElementType>(string name, IEnumerable<ElementType> channelData)
|
||||
{
|
||||
return Insert(channels.Count, name, channelData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new vertex channel to the end of the collection.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the new channel.</param>
|
||||
/// <param name="elementType">Type of data to be contained in the new channel.</param>
|
||||
/// <param name="channelData">Initial data for the new channel. If null, the channel is filled with the default value for that type.</param>
|
||||
/// <returns>The newly added vertex channel.</returns>
|
||||
public VertexChannel Add(string name, Type elementType, IEnumerable channelData)
|
||||
{
|
||||
return Insert(channels.Count, name, elementType, channelData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all vertex channels from the collection.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
channels.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the collection contains the specified vertex channel.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the channel being searched for.</param>
|
||||
/// <returns>true if the channel was found; false otherwise.</returns>
|
||||
public bool Contains(string name)
|
||||
{
|
||||
return channels.Exists(c => { return c.Name == name; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the collection contains the specified vertex channel.
|
||||
/// </summary>
|
||||
/// <param name="item">The channel being searched for.</param>
|
||||
/// <returns>true if the channel was found; false otherwise.</returns>
|
||||
public bool Contains(VertexChannel item)
|
||||
{
|
||||
return channels.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the channel, at the specified index, to another vector format.
|
||||
/// </summary>
|
||||
/// <typeparam name="TargetType">Type of the target format. Can be one of the following: Single, Vector2, Vector3, Vector4, IPackedVector</typeparam>
|
||||
/// <param name="index">Index of the channel to be converted.</param>
|
||||
/// <returns>New channel in the specified format.</returns>
|
||||
public VertexChannel<TargetType> ConvertChannelContent<TargetType>(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<TargetType> result = null;
|
||||
try
|
||||
{
|
||||
// Insert a new converted channel at the same index
|
||||
result = Insert(index, channel.Name, channel.ReadConvertedContent<TargetType>());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the channel, specified by name to another vector format.
|
||||
/// </summary>
|
||||
/// <typeparam name="TargetType">Type of the target format. Can be one of the following: Single, Vector2, Vector3, Vector4, IPackedVector</typeparam>
|
||||
/// <param name="name">Name of the channel to be converted.</param>
|
||||
/// <returns>New channel in the specified format.</returns>
|
||||
public VertexChannel<TargetType> ConvertChannelContent<TargetType>(string name)
|
||||
{
|
||||
var index = IndexOf(name);
|
||||
if (index < 0)
|
||||
throw new ArgumentException("name");
|
||||
return ConvertChannelContent<TargetType>(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex channel with the specified index and content type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of a vertex channel.</typeparam>
|
||||
/// <param name="index">Index of a vertex channel.</param>
|
||||
/// <returns>The vertex channel.</returns>
|
||||
public VertexChannel<T> Get<T>(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<T>)channel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex channel with the specified name and content type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the vertex channel.</typeparam>
|
||||
/// <param name="name">Name of a vertex channel.</param>
|
||||
/// <returns>The vertex channel.</returns>
|
||||
public VertexChannel<T> Get<T>(string name)
|
||||
{
|
||||
var index = IndexOf(name);
|
||||
if (index < 0)
|
||||
throw new ArgumentException("name");
|
||||
return Get<T>(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator that iterates through the vertex channels of a collection.
|
||||
/// </summary>
|
||||
/// <returns>Enumerator for the collection.</returns>
|
||||
public IEnumerator<VertexChannel> GetEnumerator()
|
||||
{
|
||||
return channels.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the index of a vertex channel with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the vertex channel being searched for.</param>
|
||||
/// <returns>Index of the vertex channel.</returns>
|
||||
public int IndexOf(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentNullException("name");
|
||||
return channels.FindIndex((v) => v.Name == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the index of the specified vertex channel.
|
||||
/// </summary>
|
||||
/// <param name="item">Vertex channel being searched for.</param>
|
||||
/// <returns>Index of the vertex channel.</returns>
|
||||
public int IndexOf(VertexChannel item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException("item");
|
||||
return channels.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new vertex channel at the specified position.
|
||||
/// </summary>
|
||||
/// <typeparam name="ElementType">Type of the new channel.</typeparam>
|
||||
/// <param name="index">Index for channel insertion.</param>
|
||||
/// <param name="name">Name of the new channel.</param>
|
||||
/// <param name="channelData">The new channel.</param>
|
||||
/// <returns>The inserted vertex channel.</returns>
|
||||
public VertexChannel<ElementType> Insert<ElementType>(int index, string name, IEnumerable<ElementType> 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<ElementType>(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;
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new vertex channel at the specified position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index for channel insertion.</param>
|
||||
/// <param name="name">Name of the new channel.</param>
|
||||
/// <param name="elementType">Type of the new channel.</param>
|
||||
/// <param name="channelData">Initial data for the new channel. If null, it is filled with the default value.</param>
|
||||
/// <returns>The inserted vertex channel.</returns>
|
||||
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 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified vertex channel from the collection.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the vertex channel being removed.</param>
|
||||
/// <returns>true if the channel was removed; false otherwise.</returns>
|
||||
public bool Remove(string name)
|
||||
{
|
||||
var index = IndexOf(name);
|
||||
if (index >= 0)
|
||||
{
|
||||
channels.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified vertex channel from the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The vertex channel being removed.</param>
|
||||
/// <returns>true if the channel was removed; false otherwise.</returns>
|
||||
public bool Remove(VertexChannel item)
|
||||
{
|
||||
return channels.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the vertex channel at the specified index position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the vertex channel being removed.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
channels.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new vertex channel to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">Vertex channel to be added.</param>
|
||||
void ICollection<VertexChannel>.Add(VertexChannel item)
|
||||
{
|
||||
channels.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of the collection to an array, starting at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="array">The destination array.</param>
|
||||
/// <param name="arrayIndex">The index at which to begin copying elements.</param>
|
||||
void ICollection<VertexChannel>.CopyTo(VertexChannel[] array, int arrayIndex)
|
||||
{
|
||||
channels.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which item should be inserted.</param>
|
||||
/// <param name="item">The item to insert.</param>
|
||||
void IList<VertexChannel>.Insert(int index, VertexChannel item)
|
||||
{
|
||||
channels.Insert(index, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through a collection.
|
||||
/// </summary>
|
||||
/// <returns>An object that can be used to iterate through the collection.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return channels.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// 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 Microsoft.Xna.Framework.Design;
|
||||
using Microsoft.Xna.Framework.Graphics.PackedVector;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for maintaining a vertex channel.
|
||||
/// This is a generic implementation of VertexChannel and, therefore, can handle strongly typed content data.
|
||||
/// </summary>
|
||||
public sealed class VertexChannel<T> : VertexChannel, IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable
|
||||
{
|
||||
List<T> items;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the strongly-typed list for the base class to access.
|
||||
/// </summary>
|
||||
internal override IList Items
|
||||
{
|
||||
get
|
||||
{
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of data contained in this channel.
|
||||
/// </summary>
|
||||
public override Type ElementType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element at the specified index.
|
||||
/// </summary>
|
||||
public new T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return items[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true if this object is read-only; false otherwise.
|
||||
/// </summary>
|
||||
bool ICollection<T>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of VertexChannel.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the channel.</param>
|
||||
internal VertexChannel(string name)
|
||||
: base(name)
|
||||
{
|
||||
items = new List<T>();
|
||||
}
|
||||
|
||||
static VertexChannel()
|
||||
{
|
||||
// Some platforms (such as Windows Store) don't support TypeConverter, which
|
||||
// is normally referenced with an attribute on the target type. To keep them
|
||||
// out of the main assembly, they are registered here before their use.
|
||||
|
||||
//TypeDescriptor.AddAttributes(typeof(Single), new TypeConverterAttribute(typeof(SingleTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(Vector2), new TypeConverterAttribute(typeof(Vector2TypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(Vector3), new TypeConverterAttribute(typeof(Vector3TypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(Vector4), new TypeConverterAttribute(typeof(Vector4TypeConverter)));
|
||||
//TypeDescriptor.AddAttributes(typeof(IPackedVector), new TypeConverterAttribute(typeof(PackedVectorTypeConverter)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified element is in the channel.
|
||||
/// </summary>
|
||||
/// <param name="item">Element being searched for.</param>
|
||||
/// <returns>true if the element is present; false otherwise.</returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return items.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of the channel to an array, starting at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="array">Array that will receive the copied channel elements.</param>
|
||||
/// <param name="arrayIndex">Starting index for copy operation.</param>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
items.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator interface for reading channel content.
|
||||
/// </summary>
|
||||
/// <returns>Enumeration of the channel content.</returns>
|
||||
public new IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return items.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">Item whose index is to be retrieved.</param>
|
||||
/// <returns>Index of specified item.</returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
return items.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the range of values from the enumerable into the channel.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the new elements should be inserted.</param>
|
||||
/// <param name="data">The data to insert into the channel.</param>
|
||||
internal override void InsertRange(int index, IEnumerable data)
|
||||
{
|
||||
if ((index < 0) || (index > items.Count))
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
if (data == null)
|
||||
throw new ArgumentNullException("data");
|
||||
if (!(data is IEnumerable<T>))
|
||||
throw new ArgumentException("data");
|
||||
items.InsertRange(index, (IEnumerable<T>)data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads channel content and automatically converts it to the specified vector format.
|
||||
/// </summary>
|
||||
/// <typeparam name="TargetType">Target vector format for the converted channel data.</typeparam>
|
||||
/// <returns>The converted channel data.</returns>
|
||||
public override IEnumerable<TargetType> ReadConvertedContent<TargetType>()
|
||||
{
|
||||
if (typeof(TargetType).IsAssignableFrom(typeof(T)))
|
||||
return items.Cast<TargetType>();
|
||||
|
||||
return Convert<TargetType>(items);
|
||||
}
|
||||
|
||||
private static IEnumerable<TargetType> Convert<TargetType>(IEnumerable<T> items)
|
||||
{
|
||||
// The following formats are supported:
|
||||
// - Single
|
||||
// - Vector2 Structure
|
||||
// - Vector3 Structure
|
||||
// - Vector4 Structure
|
||||
// - Any implementation of IPackedVector Interface.
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (!converter.CanConvertTo(typeof(TargetType)))
|
||||
{
|
||||
// If you got this exception, check out the static constructor above
|
||||
// to make sure your type is registered.
|
||||
throw new NotImplementedException(
|
||||
string.Format("TypeConverter for {0} -> {1} is not implemented.",
|
||||
typeof(T).Name, typeof(TargetType).Name));
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
yield return (TargetType)converter.ConvertTo(item, typeof(TargetType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new element to the end of the collection.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to add.</param>
|
||||
void ICollection<T>.Add(T value)
|
||||
{
|
||||
((ICollection<T>)Items).Add(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all elements from the collection.
|
||||
/// </summary>
|
||||
void ICollection<T>.Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a specified element from the collection.
|
||||
/// </summary>
|
||||
/// <param name="value">The element to remove.</param>
|
||||
/// <returns>true if the channel was removed; false otherwise.</returns>
|
||||
bool ICollection<T>.Remove(T value)
|
||||
{
|
||||
return ((ICollection<T>)Items).Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an element into the collection at the specified position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index at which to insert the element.</param>
|
||||
/// <param name="value">The element to insert.</param>
|
||||
void IList<T>.Insert(int index, T value)
|
||||
{
|
||||
Items.Insert(index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the specified index position.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the element to remove.</param>
|
||||
void IList<T>.RemoveAt(int index)
|
||||
{
|
||||
Items.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of values from the channel.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based starting index of the range of elements to remove.</param>
|
||||
/// <param name="count"> The number of elements to remove.</param>
|
||||
internal override void RemoveRange(int index, int count)
|
||||
{
|
||||
items.RemoveRange(index, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// 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.Globalization;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties for managing a collection of vertex channel names.
|
||||
/// </summary>
|
||||
public static class VertexChannelNames
|
||||
{
|
||||
/// <summary>
|
||||
/// A lookup for the TryDecodeUsage method.
|
||||
/// </summary>
|
||||
static Dictionary<string, VertexElementUsage> usages;
|
||||
|
||||
static VertexChannelNames()
|
||||
{
|
||||
// Populate the lookup for TryDecodeUsage
|
||||
usages = new Dictionary<string, VertexElementUsage>();
|
||||
string[] names = Enum.GetNames(typeof(VertexElementUsage));
|
||||
Array values = Enum.GetValues(typeof(VertexElementUsage));
|
||||
for (int i = 0; i < names.Length; ++i)
|
||||
usages.Add(names[i], (VertexElementUsage)values.GetValue(i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a binormal vector channel with the specified index.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Zero-based index of the vector channel being retrieved.</param>
|
||||
/// <returns>Name of the retrieved vector channel.</returns>
|
||||
public static string Binormal(int usageIndex)
|
||||
{
|
||||
return EncodeName(VertexElementUsage.Binormal, usageIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a color channel with the specified index.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Zero-based index of the color channel being retrieved.</param>
|
||||
/// <returns>Name of the retrieved color channel.</returns>
|
||||
public static string Color(int usageIndex)
|
||||
{
|
||||
return EncodeName(VertexElementUsage.Color, usageIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a channel base name stub from the encoded string format.
|
||||
/// </summary>
|
||||
/// <param name="encodedName">Encoded string to be decoded.</param>
|
||||
/// <returns>Extracted base name.</returns>
|
||||
public static string DecodeBaseName(string encodedName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(encodedName))
|
||||
throw new ArgumentNullException("encodedName");
|
||||
return encodedName.TrimEnd("0123456789".ToCharArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a channel usage index from the encoded format.
|
||||
/// </summary>
|
||||
/// <param name="encodedName">Encoded name to be decoded.</param>
|
||||
/// <returns>Resulting channel usage index.</returns>
|
||||
public static int DecodeUsageIndex(string encodedName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(encodedName))
|
||||
throw new ArgumentNullException("encodedName");
|
||||
// Extract the base name
|
||||
string baseName = DecodeBaseName(encodedName);
|
||||
if (string.IsNullOrEmpty(baseName))
|
||||
throw new InvalidOperationException("encodedName");
|
||||
|
||||
// Subtract the base name from the string and convert the remainder to an integer.
|
||||
// TryParse solves the problem when name is just 'BlendIndicies' for example, in
|
||||
// which case we default to index 0, assuming only 1 index.
|
||||
int index = 0;
|
||||
int.TryParse(encodedName.Substring(baseName.Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines a channel name stub and usage index into a string name.
|
||||
/// </summary>
|
||||
/// <param name="baseName">A channel base name stub.</param>
|
||||
/// <param name="usageIndex">A channel usage index.</param>
|
||||
/// <returns>Resulting encoded name.</returns>
|
||||
public static string EncodeName(string baseName, int usageIndex)
|
||||
{
|
||||
return baseName + usageIndex.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines a vertex declaration usage and usage index into a string name.
|
||||
/// </summary>
|
||||
/// <param name="vertexElementUsage">A vertex declaration.</param>
|
||||
/// <param name="usageIndex">An index for the vertex declaration.</param>
|
||||
/// <returns>Resulting encoded name.</returns>
|
||||
public static string EncodeName(VertexElementUsage vertexElementUsage, int usageIndex)
|
||||
{
|
||||
return vertexElementUsage.ToString() + usageIndex.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the primary normal channel.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <returns>Primary normal channel name.</returns>
|
||||
public static string Normal()
|
||||
{
|
||||
return Normal(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a normal channel with the specified index.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Zero-based index of the normal channel being retrieved.</param>
|
||||
/// <returns>Normal channel at the specified index.</returns>
|
||||
public static string Normal(int usageIndex)
|
||||
{
|
||||
return EncodeName(VertexElementUsage.Normal, usageIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a tangent vector channel with the specified index.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Zero-based index of the tangent vector channel being retrieved.</param>
|
||||
/// <returns>Name of the retrieved tangent vector channel.</returns>
|
||||
public static string Tangent(int usageIndex)
|
||||
{
|
||||
return EncodeName(VertexElementUsage.Tangent, usageIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a texture coordinate channel with the specified index.
|
||||
/// This will typically contain Vector3 data.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Zero-based index of the texture coordinate channel being retrieved.</param>
|
||||
/// <returns>Name of the retrieved texture coordinate channel.</returns>
|
||||
public static string TextureCoordinate(int usageIndex)
|
||||
{
|
||||
return EncodeName(VertexElementUsage.TextureCoordinate, usageIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vertex declaration usage enumeration from the encoded string format.
|
||||
/// </summary>
|
||||
/// <param name="encodedName">Encoded name of a vertex declaration.</param>
|
||||
/// <param name="usage">Value of the declaration usage for the vertex declaration.</param>
|
||||
/// <returns>true if the encoded name maps to a VertexElementUsage enumeration value; false otherwise.</returns>
|
||||
public static bool TryDecodeUsage(string encodedName, out VertexElementUsage usage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(encodedName))
|
||||
throw new ArgumentNullException("encodedName");
|
||||
// Extract the base name
|
||||
string baseName = DecodeBaseName(encodedName);
|
||||
if (string.IsNullOrEmpty(baseName))
|
||||
throw new InvalidOperationException("encodedName");
|
||||
return usages.TryGetValue(baseName, out usage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the primary animation weights channel.
|
||||
/// This will typically contain data on the bone weights for a vertex channel. For more information, see BoneWeightCollection.
|
||||
/// </summary>
|
||||
/// <returns>Name of the primary animation weights channel.</returns>
|
||||
public static string Weights()
|
||||
{
|
||||
return Weights(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of an animation weights channel at the specified index.
|
||||
/// This will typically contain data on the bone weights for a vertex channel. For more information, see BoneWeightCollection.
|
||||
/// </summary>
|
||||
/// <param name="usageIndex">Index of the animation weight channel to be retrieved.</param>
|
||||
/// <returns>Name of the retrieved animation weights channel.</returns>
|
||||
public static string Weights(int usageIndex)
|
||||
{
|
||||
// This appears to be the odd one out that doesn't use the VertexElementUsage enum.
|
||||
return EncodeName("Weights", usageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
// 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 Microsoft.Xna.Framework.Content.Pipeline.Processors;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Graphics.PackedVector;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods and properties for maintaining the vertex data of a GeometryContent.
|
||||
/// </summary>
|
||||
/// <remarks>This class combines a collection of arbitrarily named data channels with a list of position indices that reference the Positions collection of the parent MeshContent.</remarks>
|
||||
public sealed class VertexContent
|
||||
{
|
||||
VertexChannelCollection channels;
|
||||
VertexChannel<int> positionIndices;
|
||||
IndirectPositionCollection positions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of named vertex data channels in the VertexContent.
|
||||
/// </summary>
|
||||
/// <value>Collection of vertex data channels.</value>
|
||||
public VertexChannelCollection Channels { get { return channels; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of position indices.
|
||||
/// </summary>
|
||||
/// <value>Position of the position index being retrieved.</value>
|
||||
/// <remarks>This list adds a level of indirection between the actual triangle indices and the Positions member of the parent. This indirection preserves the topological vertex identity in cases where a single vertex position is used by triangles that straddle a discontinuity in some other data channel.
|
||||
/// For example, the following code gets the position of the first vertex of the first triangle in a GeometryContent object:
|
||||
/// parent.Positions[Vertices.PositionIndices[Indices[0]]]</remarks>
|
||||
public VertexChannel<int> PositionIndices { get { return positionIndices; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets position data from the parent mesh object.
|
||||
/// </summary>
|
||||
/// <value>Collection of vertex positions for the mesh.</value>
|
||||
/// <remarks>The collection returned from this call provides a virtualized view of the vertex positions for this batch. The collection uses the contents of the PositionIndices property to index into the parent Positions. This collection is read-only. If you need to modify any contained values, edit the PositionIndices or Positions members directly.</remarks>
|
||||
public IndirectPositionCollection Positions { get { return positions; } }
|
||||
|
||||
/// <summary>
|
||||
/// Number of vertices for the content.
|
||||
/// </summary>
|
||||
/// <value>Number of vertices.</value>
|
||||
public int VertexCount { get { return positionIndices.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a VertexContent instance.
|
||||
/// </summary>
|
||||
internal VertexContent(GeometryContent geom)
|
||||
{
|
||||
positionIndices = new VertexChannel<int>("PositionIndices");
|
||||
positions = new IndirectPositionCollection(geom, positionIndices);
|
||||
channels = new VertexChannelCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a new vertex index to the end of the PositionIndices collection.
|
||||
/// Other vertex channels will automatically be extended and the new indices populated with default values.
|
||||
/// </summary>
|
||||
/// <param name="positionIndex">Index into the MeshContent.Positions member of the parent.</param>
|
||||
/// <returns>Index of the new entry. This can be added to the Indices member of the parent.</returns>
|
||||
public int Add(int positionIndex)
|
||||
{
|
||||
return positionIndices.Items.Add(positionIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends multiple vertex indices to the end of the PositionIndices collection.
|
||||
/// Other vertex channels will automatically be extended and the new indices populated with default values.
|
||||
/// </summary>
|
||||
/// <param name="positionIndexCollection">Index into the Positions member of the parent.</param>
|
||||
public void AddRange(IEnumerable<int> positionIndexCollection)
|
||||
{
|
||||
positionIndices.InsertRange(positionIndices.Items.Count, positionIndexCollection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts design-time vertex position and channel data into a vertex buffer format that a graphics device can recognize.
|
||||
/// </summary>
|
||||
/// <returns>A packed vertex buffer.</returns>
|
||||
/// <exception cref="InvalidContentException">One or more of the vertex channel types are invalid or an unrecognized name was passed to VertexElementUsage.</exception>
|
||||
public VertexBufferContent CreateVertexBuffer()
|
||||
{
|
||||
var vertexBuffer = new VertexBufferContent(positions.Count);
|
||||
var stride = SetupVertexDeclaration(vertexBuffer);
|
||||
|
||||
// TODO: Verify enough elements in channels to match positions?
|
||||
|
||||
// Write out data in an interleaved fashion each channel at a time, for example:
|
||||
// |------------------------------------------------------------|
|
||||
// |POSITION[0] | NORMAL[0] |TEX0[0] | POSITION[1]| NORMAL[1] |
|
||||
// |-----------------------------------------------|------------|
|
||||
// #0 |111111111111|____________|________|111111111111|____________|
|
||||
// #1 |111111111111|111111111111|________|111111111111|111111111111|
|
||||
// #2 |111111111111|111111111111|11111111|111111111111|111111111111|
|
||||
|
||||
// #0: Write position vertices using stride to skip over the other channels:
|
||||
vertexBuffer.Write(0, stride, positions);
|
||||
|
||||
var channelOffset = VertexBufferContent.SizeOf(typeof(Vector3));
|
||||
foreach (var channel in Channels)
|
||||
{
|
||||
// #N: Fill in the channel within each vertex
|
||||
var channelType = channel.ElementType;
|
||||
vertexBuffer.Write(channelOffset, stride, channelType, channel);
|
||||
channelOffset += VertexBufferContent.SizeOf(channelType);
|
||||
}
|
||||
|
||||
return vertexBuffer;
|
||||
}
|
||||
|
||||
private int SetupVertexDeclaration(VertexBufferContent result)
|
||||
{
|
||||
var offset = 0;
|
||||
|
||||
// We always have a position channel
|
||||
result.VertexDeclaration.VertexElements.Add(new VertexElement(offset, VertexElementFormat.Vector3,
|
||||
VertexElementUsage.Position, 0));
|
||||
offset += VertexElementFormat.Vector3.GetSize();
|
||||
|
||||
// Optional channels
|
||||
foreach (var channel in Channels)
|
||||
{
|
||||
VertexElementFormat format;
|
||||
VertexElementUsage usage;
|
||||
|
||||
// Try to determine the vertex format
|
||||
if (channel.ElementType == typeof(Single))
|
||||
format = VertexElementFormat.Single;
|
||||
else if (channel.ElementType == typeof(Vector2))
|
||||
format = VertexElementFormat.Vector2;
|
||||
else if (channel.ElementType == typeof(Vector3))
|
||||
format = VertexElementFormat.Vector3;
|
||||
else if (channel.ElementType == typeof(Vector4))
|
||||
format = VertexElementFormat.Vector4;
|
||||
else if (channel.ElementType == typeof(Color))
|
||||
format = VertexElementFormat.Color;
|
||||
else if (channel.ElementType == typeof(Byte4))
|
||||
format = VertexElementFormat.Byte4;
|
||||
else if (channel.ElementType == typeof(Short2))
|
||||
format = VertexElementFormat.Short2;
|
||||
else if (channel.ElementType == typeof(Short4))
|
||||
format = VertexElementFormat.Short4;
|
||||
else if (channel.ElementType == typeof(NormalizedShort2))
|
||||
format = VertexElementFormat.NormalizedShort2;
|
||||
else if (channel.ElementType == typeof(NormalizedShort4))
|
||||
format = VertexElementFormat.NormalizedShort4;
|
||||
else if (channel.ElementType == typeof(HalfVector2))
|
||||
format = VertexElementFormat.HalfVector2;
|
||||
else if (channel.ElementType == typeof(HalfVector4))
|
||||
format = VertexElementFormat.HalfVector4;
|
||||
else
|
||||
throw new InvalidContentException(string.Format("Unrecognized vertex content type: '{0}'", channel.ElementType));
|
||||
|
||||
// Try to determine the vertex usage
|
||||
if (!VertexChannelNames.TryDecodeUsage(channel.Name, out usage))
|
||||
throw new InvalidContentException(string.Format("Unknown vertex element usage for channel '{0}'", channel.Name));
|
||||
|
||||
// Try getting the usage index
|
||||
var usageIndex = VertexChannelNames.DecodeUsageIndex(channel.Name);
|
||||
|
||||
result.VertexDeclaration.VertexElements.Add(new VertexElement(offset, format, usage, usageIndex));
|
||||
offset += format.GetSize();
|
||||
}
|
||||
|
||||
result.VertexDeclaration.VertexStride = offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new vertex index to the PositionIndices collection.
|
||||
/// Other vertex channels will automatically be extended and the new indices populated with default values.
|
||||
/// </summary>
|
||||
/// <param name="index">Vertex index to be inserted.</param>
|
||||
/// <param name="positionIndex">Position of new vertex index in the collection.</param>
|
||||
public void Insert(int index, int positionIndex)
|
||||
{
|
||||
positionIndices.Items.Insert(index, positionIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts multiple vertex indices to the PositionIndices collection.
|
||||
/// Other vertex channels will automatically be extended and the new indices populated with default values.
|
||||
/// </summary>
|
||||
/// <param name="index">Vertex index to be inserted.</param>
|
||||
/// <param name="positionIndexCollection">Position of the first element of the inserted range in the collection.</param>
|
||||
public void InsertRange(int index, IEnumerable<int> positionIndexCollection)
|
||||
{
|
||||
positionIndices.InsertRange(index, positionIndexCollection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a vertex index from the specified location in both PositionIndices and VertexChannel<T>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the vertex to be removed.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= VertexCount)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
|
||||
positionIndices.Items.RemoveAt(index);
|
||||
|
||||
foreach (var channel in channels)
|
||||
channel.Items.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of vertex indices from the specified location in both PositionIndices and VertexChannel<T>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the first vertex index to be removed.</param>
|
||||
/// <param name="count">Number of indices to remove.</param>
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
if (index < 0 || index >= VertexCount)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
if (count < 0 || (index+count) > VertexCount)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
positionIndices.RemoveRange(index, count);
|
||||
|
||||
foreach (var channel in channels)
|
||||
channel.RemoveRange(index, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user