PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/Graphics/VertexElement.cs

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