PageRenderTime 128ms CodeModel.GetById 60ms app.highlight 32ms RepoModel.GetById 29ms app.codeStats 0ms

/Utilities/Graphics/VertexElement.cs

#
C# | 851 lines | 544 code | 77 blank | 230 comment | 18 complexity | 63f2bedd4ebfe2c895757cf38c8d44ff MD5 | raw file
  1using System;
  2using System.IO;
  3using Delta.Utilities.Datatypes;
  4using NUnit.Framework;
  5
  6namespace Delta.Utilities.Graphics
  7{
  8	/// <summary>
  9	/// Vertex element, defines the type of a vertex element, if it is
 10	/// compressed and the size occupied. See VertexFormat for details.
 11	/// </summary>
 12	public struct VertexElement : ISaveLoadBinary
 13	{
 14		#region Constants
 15		/// <summary>
 16		/// The max. distance (in meters) that a vertex may be distant from the
 17		/// coordinate origin (because of mobile vertex compression). When
 18		/// importing 3D models this is checked and a warning will be shown if a
 19		/// vertex is farther away from the origin than 300m.
 20		/// See Position3DCompressionValue for the formula.
 21		/// </summary>
 22		public const float MaxCompressedVertexDistance =
 23			//this is a good value, big and nice, but does not help for Soulcraft: 300.0f;
 24			// Soulcraft has animated models with -1100 to +1500 units, even with centering we
 25			// would need -1200 to +1200 to make the crappy animated files work (we cannot
 26			// center however as animated matrices do not know about it).
 27			1600.0f;
 28
 29		//300.0f;
 30		//soulcraft tech demo camera fix: 267//75//64//127
 31
 32		/// <summary>
 33		/// When converting vertex positions to shorts to store them into
 34		/// Position3DCompressed this formula is used (1/436.0f). The minimum
 35		/// resulting 3D position is -75.0f and the maximum is 75.0f. Everything
 36		/// beyond that cannot be represented as Position3DCompressed shorts, but
 37		/// since most models are only a few meters in size this should be no
 38		/// problem.
 39		/// </summary>
 40		public const float Position3DCompressionValue =
 41			(short.MaxValue) / MaxCompressedVertexDistance;
 42
 43		//for 267: 122.72, for 75: 436.0f;//for 64: 512.0f;
 44
 45		/// <summary>
 46		/// When converting vertex positions to shorts to store them into
 47		/// Position2DCompressed this formula is used (1/16384). The minimum
 48		/// valid value for shorts is -32767, divided through this constant this
 49		/// is -2.0f, the maximum value is 32768, which is +2.0f.
 50		/// </summary>
 51		public const float Position2DCompressionValue = (short.MaxValue / 2);
 52
 53		/// <summary>
 54		/// For TextureUVCompressed we just use a simple 1/32767 formula, since
 55		/// we will only accept UVs between 0 and 1 anyway. This precision is also
 56		/// good for the bigger atlas textures we might have (up to 2k/2k).
 57		/// </summary>
 58		public const float ShortCompressionValue = short.MaxValue;
 59
 60		/// <summary>
 61		/// For normals, tangents and binormals (colors are not normalized, they
 62		/// won't need any compression, we can just use the bytes values from
 63		/// our Color struct). So only for normalized values we use a simple
 64		/// compression formula: value * 127
 65		/// Decompress formula: value / 127
 66		/// The formula is only for values between -1 and +1.
 67		/// </summary>
 68		private const float SignedByteCompressionValue = sbyte.MaxValue;
 69
 70		/// <summary>
 71		/// OpenGL attributes are used for the shader generation and for getting
 72		/// of the attributes to set them.
 73		/// </summary>
 74		public const string OpenGLPositionAttribute = "vPosition";
 75
 76		/// <summary>
 77		/// Open GL tex coord attribute
 78		/// </summary>
 79		public const string OpenGLTexCoordAttribute = "vTexCoord";
 80
 81		/// <summary>
 82		/// Open GL normal attribute
 83		/// </summary>
 84		public const string OpenGLNormalAttribute = "vNormal";
 85
 86		/// <summary>
 87		/// Open GL tangent attribute
 88		/// </summary>
 89		public const string OpenGLTangentAttribute = "vTangent";
 90
 91		public const string OpenGLBinormalAttribute = "vBinormal";
 92
 93		public const string OpenGLColorAttribute = "vColor";
 94
 95		public const string OpenGLLightMapTexCoordAttribute = "vLightTexCoord";
 96
 97		public const string OpenGLExtraTexCoordAttribute = "vExtraTexCoord";
 98
 99		public const string OpenGLSkinWeightsAttribute = "vSkinWeights";
100
101		public const string OpenGLSkinIndicesAttribute = "vSkinIndices";
102		#endregion
103
104		#region Type (Public)
105		/// <summary>
106		/// Type for this vertex element (position, uv, etc.)
107		/// </summary>
108		public VertexElementType Type
109		{
110			get;
111			private set;
112		}
113		#endregion
114
115		#region IsCompressed (Public)
116		/// <summary>
117		/// like color and skin data are already compressed and cannot be more
118		/// compact. Most VertexFormats like Position2DTexture really get much
119		/// smaller with compressed on (down to 10 bytes from 20 uncompressed).
120		/// </summary>
121		public bool IsCompressed
122		{
123			get;
124			private set;
125		}
126		#endregion
127
128		#region Size (Public)
129		/// <summary>
130		/// Size in bytes of this vertex element. Used to find the offset for the
131		/// next element mostly and for the total vertex size in bytes of course.
132		/// </summary>
133		public int Size
134		{
135			get;
136			private set;
137		}
138		#endregion
139
140		#region Offset (Public)
141		/// <summary>
142		/// Offset from the start of the vertex data to this element in bytes.
143		/// Calculated in VertexFormat constructor.
144		/// </summary>
145		public int Offset
146		{
147			get;
148			internal set;
149		}
150		#endregion
151
152		#region ComponentCount (Public)
153		/// <summary>
154		/// Returns the number of components of that element.
155		/// E.g. Color = 4, Vector (Position, Normal) = 3, Point (UV) = 2.
156		/// </summary>
157		public int ComponentCount
158		{
159			get;
160			private set;
161		}
162		#endregion
163
164		#region IsNormalized (Public)
165		/// <summary>
166		/// Is normalized? Currently used for compressed Texture UVs, Normals,
167		/// Tangents, Color and SkinWeights. This is used to save bandwidth
168		/// because compressed vertex data is smaller, but needs to be normalized
169		/// usually (except when we account for it in the shader like for
170		/// position vertex data via the World or WorldViewProj matrices).
171		/// </summary>
172		public bool IsNormalized
173		{
174			get;
175			private set;
176		}
177		#endregion
178
179		#region IsByteFormat (Public)
180		/// <summary>
181		/// Is byte format? Currently only used for colors. Use IsSignedByteFormat
182		/// for normals, tangents and binormals.
183		/// </summary>
184		public bool IsByteFormat
185		{
186			get;
187			private set;
188		}
189		#endregion
190
191		#region IsSignedByteFormat (Public)
192		/// <summary>
193		/// Is signed byte format? Only used for normals, tangents, binormals.
194		/// Colors use IsByteFormat (unsigned).
195		/// </summary>
196		public bool IsSignedByteFormat
197		{
198			get;
199			private set;
200		}
201		#endregion
202
203		#region IsShortFormat (Public)
204		/// <summary>
205		/// Is short format? Used mostly for compressed vertex data. If this
206		/// and IsByteFormat are false the vertex element components are floats!
207		/// </summary>
208		public bool IsShortFormat
209		{
210			get;
211			private set;
212		}
213		#endregion
214
215		#region Constructors
216		/// <summary>
217		/// for vertex elements to save bandwidth and make rendering much faster.
218		/// </summary>
219		public VertexElement(VertexElementType elementType)
220			: this()
221		{
222			Type = elementType;
223			IsCompressed = false;
224			SetupProperties();
225		}
226
227		/// <summary>
228		/// Create vertex element, this constructor forces compression if wanted.
229		/// This is usually controlled by the content and build system on the
230		/// improve rendering performance).
231		/// </summary>
232		public VertexElement(VertexElementType elementType, bool setIsCompressed)
233			: this()
234		{
235			Type = elementType;
236			IsCompressed = setIsCompressed;
237			SetupProperties();
238		}
239		#endregion
240
241		#region ISaveLoadBinary Members
242		/// <summary>
243		/// Load
244		/// </summary>
245		public void Load(BinaryReader reader)
246		{
247			Type = (VertexElementType)reader.ReadInt32();
248			IsCompressed = reader.ReadBoolean();
249			Size = reader.ReadInt32();
250			Offset = reader.ReadInt32();
251			SetupProperties();
252		}
253
254		/// <summary>
255		/// Save
256		/// </summary>
257		public void Save(BinaryWriter writer)
258		{
259			writer.Write((int)Type);
260			writer.Write(IsCompressed);
261			writer.Write(Size);
262			writer.Write(Offset);
263		}
264		#endregion
265
266		#region LoadData (Public)
267		/// <summary>
268		/// Extract value as a Vector, not very efficient, but always works :)
269		/// </summary>
270		/// <param name="reader">Reader</param>
271		public Vector LoadData(BinaryReader reader)
272		{
273			// We have to check each type and if the data is compressed
274			switch (Type)
275			{
276				case VertexElementType.Position2D:
277					return (IsCompressed)
278					       	? // If data is compressed, decompress it
279					       new Vector(
280					       	Position2DDecompress(reader.ReadInt16()),
281					       	Position2DDecompress(reader.ReadInt16()), 0.0f)
282					       	: // else we can just take uncompressed data
283					       new Vector(new Point(reader), 0.0f);
284
285				case VertexElementType.Position3D:
286					if (IsCompressed)
287					{
288						// If data is compressed, decompress it
289						Vector vec = new Vector(
290							Position3DDecompress(reader.ReadInt16()),
291							Position3DDecompress(reader.ReadInt16()),
292							Position3DDecompress(reader.ReadInt16()));
293						// Since we write 2 extra bytes to the stream, 
294						// should we also read 2 extra bytes? Check SaveData.
295						reader.ReadInt16();
296						return vec;
297					}
298					else
299					{
300						// else we can just take uncompressed data
301						return new Vector(reader);
302					}
303
304				case VertexElementType.Normal:
305				case VertexElementType.Tangent:
306				case VertexElementType.Binormal:
307				case VertexElementType.TextureUVW:
308					if (IsCompressed)
309					{
310						// Read three bytes for X, Y, Z
311						Vector vec = new Vector(
312							SignedByteDecompress(reader.ReadSByte()),
313							SignedByteDecompress(reader.ReadSByte()),
314							SignedByteDecompress(reader.ReadSByte()));
315
316						// Read an extra byte.
317						reader.ReadSByte();
318
319						return vec;
320					}
321					else
322					{
323						return new Vector(reader);
324					}
325
326				case VertexElementType.TextureUV:
327				case VertexElementType.LightMapUV:
328				case VertexElementType.ExtraUV:
329					return (IsCompressed)
330					       	? // If data is compressed, decompress it
331					       new Vector(
332					       	ShortDecompress(reader.ReadInt16()),
333					       	ShortDecompress(reader.ReadInt16()), 0.0f)
334					       	: // else we can just take uncompressed data
335					       new Vector(new Point(reader), 0.0f);
336
337				case VertexElementType.Color:
338					// Color data is always compressed (4 bytes), but we only return a
339					// vector here, so only return RGB.
340					Color color = new Color(reader);
341					return new Vector(color.R, color.G, color.B);
342
343				case VertexElementType.SkinIndices:
344					// Skinned data is always compressed (2 shorts)
345					return new Vector(
346						// Use the short data directly, no conversion or normalization.
347						reader.ReadUInt16(),
348						reader.ReadUInt16(), 0.0f);
349				case VertexElementType.SkinWeights:
350					// Skinned data is always compressed (2 shorts)
351					return new Vector(
352						ShortDecompress(reader.ReadInt16()),
353						ShortDecompress(reader.ReadInt16()), 0.0f);
354
355				default:
356					// Unsupported vertex type
357					throw new NotSupportedException("Invalid vertex type: " + Type);
358			}
359		}
360		#endregion
361
362		#region LoadDataAsPoint (Public)
363		/// <summary>
364		/// Extract value as a Point, not very efficient, but always works :)
365		/// </summary>
366		/// <param name="reader">Reader</param>
367		public Point LoadDataAsPoint(BinaryReader reader)
368		{
369			// We have to check each type and if the data is compressed
370			switch (Type)
371			{
372				case VertexElementType.Position2D:
373					// If data is compressed, decompress it, else take uncompressed data
374					return
375						IsCompressed
376							? new Point(
377							  	Position2DDecompress(reader.ReadInt16()),
378							  	Position2DDecompress(reader.ReadInt16()))
379							: new Point(reader);
380
381				case VertexElementType.TextureUV:
382				case VertexElementType.LightMapUV:
383				case VertexElementType.ExtraUV:
384					// If data is compressed, decompress it, else take uncompressed data
385					return
386						IsCompressed
387							? new Point(
388							  	ShortDecompress(reader.ReadInt16()),
389							  	ShortDecompress(reader.ReadInt16()))
390							: new Point(reader);
391
392				case VertexElementType.SkinIndices:
393					// Skinned data is always compressed (2 shorts)
394					return new Point(
395						// Use the short data directly, no conversion or normalization.
396						reader.ReadUInt16(),
397						reader.ReadUInt16());
398				case VertexElementType.SkinWeights:
399					// Skinned data is always compressed (2 shorts)
400					return new Point(
401						ShortDecompress(reader.ReadInt16()),
402						ShortDecompress(reader.ReadInt16()));
403
404				default:
405					// Unsupported vertex type
406					Log.Warning("Invalid vertex type: " + Type);
407					return Point.Zero;
408			}
409		}
410		#endregion
411
412		#region SaveData (Public)
413		/// <summary>
414		/// Save data helper method for GeometryData.SetVertexData, which is slow,
415		/// but still helpful for debugging and creating geometry dynamically.
416		/// </summary>
417		public void SaveData(BinaryWriter writer, Vector data)
418		{
419			// We have to check each type and if the data is compressed
420			switch (Type)
421			{
422				case VertexElementType.Position2D:
423					if (IsCompressed)
424					{
425						// Write data compressed
426						writer.Write(Position2DCompress(data.X));
427						writer.Write(Position2DCompress(data.Y));
428					}
429					else
430					{
431						data.ToPoint().Save(writer);
432					}
433					break;
434
435				case VertexElementType.Position3D:
436					if (IsCompressed)
437					{
438						// Write data compressed
439						writer.Write(Position3DCompress(data.X));
440						writer.Write(Position3DCompress(data.Y));
441						writer.Write(Position3DCompress(data.Z));
442
443						// Writing 2 extra bytes to the stream to make it 8 bytes long
444						// instead of 6, it is needed for the compression to work with
445						// XNA, but also very good for OpenGL and DirectX (more optimized
446						// to be 4 byte aligned like everything else).
447						writer.Write((short)0);
448					}
449					else
450					{
451						data.Save(writer);
452					}
453					break;
454
455				case VertexElementType.Normal:
456				case VertexElementType.Tangent:
457				case VertexElementType.Binormal:
458				case VertexElementType.TextureUVW:
459					if (IsCompressed)
460					{
461						// Write data compressed
462						writer.Write(SignedByteCompress(data.X));
463						writer.Write(SignedByteCompress(data.Y));
464						writer.Write(SignedByteCompress(data.Z));
465
466						// Writing 1 extra bytes to the stream to make it 4 bytes long 
467						// instead of 3, it's needed for the compression to work with XNA.
468						writer.Write((sbyte)0);
469					}
470					else
471					{
472						data.Save(writer);
473					}
474					break;
475
476				case VertexElementType.TextureUV:
477				case VertexElementType.LightMapUV:
478				case VertexElementType.ExtraUV:
479					if (IsCompressed)
480					{
481						// Write data compressed
482						writer.Write(ShortCompress(data.X));
483						writer.Write(ShortCompress(data.Y));
484					}
485					else
486					{
487						data.ToPoint().Save(writer);
488					}
489					break;
490
491				case VertexElementType.Color:
492					// Color data is always compressed (4 bytes), but we only return a
493					// vector here, so only use RGB. Note: Use SaveData(Color) overload!
494					new Color(data.X, data.Y, data.Z, 1.0f).Save(writer);
495					break;
496
497				case VertexElementType.SkinIndices:
498					// Skinned data is always compressed (2 shorts)
499					writer.Write((short)data.X);
500					writer.Write((short)data.Y);
501					break;
502				case VertexElementType.SkinWeights:
503					// Skinned data is always compressed (2 shorts)
504					writer.Write(ShortCompress(data.X));
505					writer.Write(ShortCompress(data.Y));
506					break;
507
508				default:
509					// Unsupported vertex type
510					Log.Warning("Invalid vertex type: " + Type);
511					break;
512			}
513		}
514
515		/// <summary>
516		/// Save data helper method for GeometryData.SetVertexData, which is slow,
517		/// but still helpful for debugging and creating geometry dynamically.
518		/// </summary>
519		public void SaveData(BinaryWriter writer, Point data)
520		{
521			// We have to check each type and if the data is compressed
522			switch (Type)
523			{
524				case VertexElementType.Position2D:
525					if (IsCompressed)
526					{
527						// Write data compressed
528						writer.Write(Position2DCompress(data.X));
529						writer.Write(Position2DCompress(data.Y));
530					}
531					else
532					{
533						data.Save(writer);
534					}
535					break;
536
537				case VertexElementType.TextureUV:
538				case VertexElementType.LightMapUV:
539				case VertexElementType.ExtraUV:
540					if (IsCompressed)
541					{
542						// Write data compressed
543						writer.Write(ShortCompress(data.X));
544						writer.Write(ShortCompress(data.Y));
545					}
546					else
547					{
548						data.Save(writer);
549					}
550					break;
551
552				case VertexElementType.SkinIndices:
553					// Skinned data is always compressed (2 shorts)
554					writer.Write((short)data.X);
555					writer.Write((short)data.Y);
556					break;
557				case VertexElementType.SkinWeights:
558					// Skinned data is always compressed (2 shorts)
559					writer.Write(ShortCompress(data.X));
560					writer.Write(ShortCompress(data.Y));
561					break;
562
563				default:
564					// Unsupported vertex type
565					Log.Warning("Invalid vertex type for " +
566					            "SaveData with point: " + Type);
567					break;
568			}
569		}
570
571		/// <summary>
572		/// Save data helper method for GeometryData.SetVertexData, which is slow,
573		/// but still helpful for debugging and creating geometry dynamically.
574		/// </summary>
575		public void SaveData(BinaryWriter writer, Color data)
576		{
577			// We have to check each type and if the data is compressed
578			switch (Type)
579			{
580				case VertexElementType.Color:
581					data.Save(writer);
582					break;
583
584				default:
585					// Unsupported vertex type
586					Log.Warning("Invalid vertex type for " +
587					            "SaveData with color: " + Type);
588					break;
589			}
590		}
591		#endregion
592
593		#region ToString (Public)
594		/// <summary>
595		/// To string
596		/// </summary>
597		public override string ToString()
598		{
599			return GetType().Name + "(Type=" + Type + ", IsCompressed=" +
600			       IsCompressed + ")";
601		}
602		#endregion
603
604		#region Methods (Private)
605
606		#region SetupProperties
607		/// <summary>
608		/// Get size, component count, vertex data format and if this vertex data
609		/// needs to be normalized (for compressed data).
610		/// </summary>
611		private void SetupProperties()
612		{
613			switch (Type)
614			{
615				case VertexElementType.Position3D:
616				case VertexElementType.TextureUVW:
617					// 3 shorts for compressed data, 3 floats uncompressed
618					// Note: 3 shorts are 6 bytes, but we align it to 8 bytes to be
619					// more efficient on many platforms that expect 4 byte alignment.
620					// In Xna for example it is only allowed to have 4 byte alignment!
621					Size = IsCompressed
622					       	? 8
623					       	: 12;
624					ComponentCount = 3;
625					IsNormalized = false;
626					IsByteFormat = false;
627					IsSignedByteFormat = false;
628					IsShortFormat = IsCompressed
629					                	? true
630					                	: false;
631					break;
632
633				case VertexElementType.Normal:
634				case VertexElementType.Tangent:
635				case VertexElementType.Binormal:
636					// Trying out 3 bytes.
637					Size = IsCompressed
638					       	? 4
639					       	: 12;
640					ComponentCount = 3;
641					if (IsCompressed)
642					{
643						IsNormalized = true;
644						IsByteFormat = false;
645						IsSignedByteFormat = true;
646					}
647					else
648					{
649						IsNormalized = false;
650						IsByteFormat = false;
651						IsSignedByteFormat = false;
652					}
653					IsShortFormat = false;
654					break;
655
656				case VertexElementType.Position2D:
657					// 2 shorts for compressed data, 2 floats uncompressed
658					Size = IsCompressed
659					       	? 4
660					       	: 8;
661					ComponentCount = 2;
662					IsNormalized = false;
663					IsByteFormat = false;
664					IsSignedByteFormat = false;
665					IsShortFormat = IsCompressed
666					                	? true
667					                	: false;
668					break;
669
670				case VertexElementType.TextureUV:
671				case VertexElementType.LightMapUV:
672				case VertexElementType.ExtraUV:
673					// 2 shorts for compressed data, 2 floats uncompressed
674					Size = IsCompressed
675					       	? 4
676					       	: 8;
677					ComponentCount = 2;
678					IsNormalized = IsCompressed
679					               	? true
680					               	: false;
681					IsByteFormat = false;
682					IsSignedByteFormat = false;
683					IsShortFormat = IsCompressed
684					                	? true
685					                	: false;
686					break;
687
688				case VertexElementType.Color:
689					// Skinned and color data is already compressed (4 bytes)
690					Size = 4;
691					ComponentCount = 4;
692					IsNormalized = true;
693					IsByteFormat = true;
694					IsSignedByteFormat = false;
695					IsShortFormat = false;
696					break;
697
698				case VertexElementType.SkinIndices:
699				case VertexElementType.SkinWeights:
700					// Skinned and color data is already compressed (2 shorts)
701					Size = 4;
702					ComponentCount = 2;
703					// Only skin weights need to be normalized, indices are fine.
704					IsNormalized = Type == VertexElementType.SkinWeights;
705					// decompress weights in vertex shader, indices are not normalized!
706					IsByteFormat = false;
707					IsSignedByteFormat = false;
708					IsShortFormat = true;
709					if (IsCompressed == false)
710					{
711						Log.Warning(
712							"The vertex type '" + Type + "' can not be used as " +
713							"uncompressed data !");
714					}
715					break;
716
717				default:
718					Log.Warning(
719						"VertexElement Type " + Type + " is not supported, unable to " +
720						"SetupProperties!");
721					break;
722			}
723		}
724		#endregion
725
726		#region Position3DCompress
727		/// <summary>
728		/// Short compression is only for values between -75.0 and +75.0.
729		/// </summary>
730		private short Position3DCompress(float originalValue)
731		{
732			return (short)Math.Round(originalValue * Position3DCompressionValue);
733		}
734		#endregion
735
736		#region Position2DCompress
737		/// <summary>
738		/// Position 2D compression
739		/// </summary>
740		private short Position2DCompress(float originalValue)
741		{
742			return (short)Math.Round(originalValue * Position2DCompressionValue);
743		}
744		#endregion
745
746		#region ShortCompress
747		/// <summary>
748		/// Short compression is only for values between -1.0 and +1.0.
749		/// </summary>
750		private short ShortCompress(float originalValue)
751		{
752			return (short)Math.Round(originalValue * ShortCompressionValue);
753		}
754		#endregion
755
756		#region SignedByteCompress
757		/// <summary>
758		/// Byte compression is only for normalized values between -1.0 and +1.0.
759		/// </summary>
760		private sbyte SignedByteCompress(float originalValue)
761		{
762			// Note: We are ignoring the -128 issue (this will go to max. -127)
763			return (sbyte)Math.Round(originalValue * SignedByteCompressionValue);
764		}
765		#endregion
766
767		#region Position3DDecompress
768		/// <summary>
769		/// Decompress a short value to a float.
770		/// Return min value: -75.0
771		/// Return max value: +75.0
772		/// </summary>
773		private float Position3DDecompress(short compressedValue)
774		{
775			return compressedValue / Position3DCompressionValue;
776		}
777		#endregion
778
779		#region Position2DDecompress
780		/// <summary>
781		/// Position 2D decompress
782		/// </summary>
783		private float Position2DDecompress(short compressedValue)
784		{
785			return compressedValue / Position2DCompressionValue;
786		}
787		#endregion
788
789		#region ShortDecompress
790		/// <summary>
791		/// Short decompression.
792		/// </summary>
793		private float ShortDecompress(short compressedValue)
794		{
795			return compressedValue / ShortCompressionValue;
796		}
797		#endregion
798
799		#region SignedByteDecompress
800		/// <summary>
801		/// Decompress a byte value to a float (for normals, tangents, etc.)
802		/// Return min value: -1.0
803		/// Return max value: +1.0
804		/// </summary>
805		private float SignedByteDecompress(sbyte compressedValue)
806		{
807			// Note: We are ignoring the -128 issue (this will go to max. -127)
808			return compressedValue / SignedByteCompressionValue;
809		}
810		#endregion
811
812		#endregion
813
814		/// <summary>
815		/// Tests
816		/// </summary>
817		internal class VertexElementTests
818		{
819			#region GetSize
820			/// <summary>
821			/// Get size
822			/// </summary>
823			[Test]
824			public void GetSize()
825			{
826				Assert.Equal(8,
827					new VertexElement(VertexElementType.Position2D, false).Size);
828				Assert.Equal(4,
829					new VertexElement(VertexElementType.Position2D, true).Size);
830				Assert.Equal(12,
831					new VertexElement(VertexElementType.Position3D, false).Size);
832				Assert.Equal(8,
833					new VertexElement(VertexElementType.Position3D, true).Size);
834				Assert.Equal(12,
835					new VertexElement(VertexElementType.Normal, false).Size);
836				Assert.Equal(4,
837					new VertexElement(VertexElementType.Normal, true).Size);
838				Assert.Equal(8,
839					new VertexElement(VertexElementType.TextureUV, false).Size);
840				Assert.Equal(4,
841					new VertexElement(VertexElementType.TextureUV, true).Size);
842				// Compressed or not makes no difference for Color, its always 4 bytes
843				Assert.Equal(4,
844					new VertexElement(VertexElementType.Color, false).Size);
845				Assert.Equal(4,
846					new VertexElement(VertexElementType.Color, true).Size);
847			}
848			#endregion
849		}
850	}
851}