PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/ContentSystem/Rendering/MeshData.cs

#
C# | 319 lines | 173 code | 23 blank | 123 comment | 16 complexity | 89a9a1da2124d87a43b507e87a299c30 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Delta.Utilities;
  5. using Delta.Utilities.Datatypes;
  6. using Delta.Utilities.Helpers;
  7. namespace Delta.ContentSystem.Rendering
  8. {
  9. /// <summary>
  10. /// Model data for imported 3D models (from FBX, Collada, 3DS, OBJ, or DXF
  11. /// files). Contains a list of geometries for each mesh in the file (often
  12. /// just one mesh, but can be more complex) and a list of materials used
  13. /// in those geometry meshes.
  14. /// </summary>
  15. public class MeshData : Content, ISaveLoadBinary
  16. {
  17. #region Constants
  18. /// <summary>
  19. /// Version number for this ModelData. If this goes above 1, we need
  20. /// to support loading older versions as well. Saving is always going
  21. /// to be the latest version (this one). Version 1, 2 and 3 were used
  22. /// early in 2011 for combined meshes with material and animation data
  23. /// baked into it. Now with version 4 and higher for v0.8.7 and up meshes
  24. /// only contain geometry, bones, offset and a link to the used material
  25. /// content file (which contains the textures and shaders used).
  26. /// </summary>
  27. private const int VersionNumber = 4;
  28. #endregion
  29. #region Get (Static)
  30. /// <summary>
  31. /// Get and load content based on the content name. This method makes sure
  32. /// we do not load the same content twice (the constructor is protected).
  33. /// </summary>
  34. /// <param name="contentName">Content name we want to load, this is
  35. /// passed onto the Content System, which will do the actual loading with
  36. /// help of the Load method in this class.</param>
  37. /// <returns>The loaded Content object, always unique for the same
  38. /// name, this helps comparing data.</returns>
  39. public static MeshData Get(string contentName)
  40. {
  41. return Get<MeshData>(contentName, ContentType.Mesh);
  42. }
  43. #endregion
  44. #region GetAllContentNames (Static)
  45. /// <summary>
  46. /// Get list of all mesh content names available in the current project.
  47. /// </summary>
  48. /// <returns>List of all mesh content names</returns>
  49. public static string[] GetAllContentNames()
  50. {
  51. Dictionary<string, ContentMetaData> meshContents =
  52. ContentManager.GetAllContentMetaData(ContentType.Mesh);
  53. return ArrayHelper.ToArray(meshContents.Keys);
  54. }
  55. #endregion
  56. #region Geometry (Public)
  57. /// <summary>
  58. /// Geometries used in this model. Usually we just have one mesh geometry.
  59. /// Note: One difference from 3D Studio is that a Geometry mesh in our
  60. /// engine is only allowed to be used by one material with one shader,
  61. /// if a multi-material mesh was exported, it will be split up into
  62. /// several Geometries with different MaterialIds (see Materials).
  63. /// </summary>
  64. public GeometryData Geometry
  65. {
  66. get;
  67. private set;
  68. }
  69. #endregion
  70. #region Material (Public)
  71. /// <summary>
  72. /// Materials used in the Geometries above. Often we just have one
  73. /// material. Please note that we optimize the list of materials and
  74. /// geometries, no duplicates are allowed. This list is even used for
  75. /// levels where many meshes with the same material are merged together.
  76. /// A material is the same if all values are equal, which can easily
  77. /// happen because of texture atlas optimizations merging many textures.
  78. /// </summary>
  79. public MaterialData Material
  80. {
  81. get;
  82. private set;
  83. }
  84. #endregion
  85. #region HasBones (Public)
  86. /// <summary>
  87. /// Has bone data for animations? Note: The animations are in MeshAnimation.
  88. /// </summary>
  89. public bool HasBones
  90. {
  91. get
  92. {
  93. // Just check if the geometry has bones
  94. return Geometry.Bones.Length > 0;
  95. }
  96. }
  97. #endregion
  98. #region PositionOffset (Public)
  99. /// <summary>
  100. /// The offset translation of the mesh to represent its local space.
  101. /// This is both important for optimizations and fixing not-centered meshes
  102. /// and also good for rendering out meshes optimized and allowing merging
  103. /// easily if needed.
  104. /// </summary>
  105. public Vector PositionOffset;
  106. #endregion
  107. #region Animations (Public)
  108. /// <summary>
  109. /// Mesh Animation data for each specific mesh. Note: This is not loaded or
  110. /// set here when loading content. Animations are always loaded and set by
  111. /// the Model class (but you can do whatever you want in your own code).
  112. /// Also notice that setting the active Animation and switching between
  113. /// them all happens in the Model class as well.
  114. /// <para />
  115. /// Note: Using the same MeshData with different Models using it with
  116. /// different animations is currently not supported and might cause
  117. /// trouble.
  118. /// </summary>
  119. public List<MeshAnimationData> Animations = new List<MeshAnimationData>();
  120. #endregion
  121. #region Constructors
  122. /// <summary>
  123. /// Create mesh data from a model content name, use Get to call this.
  124. /// </summary>
  125. /// <param name="meshContentName">Name of the mesh content.</param>
  126. protected MeshData(string meshContentName)
  127. : base(meshContentName, ContentType.Mesh)
  128. {
  129. }
  130. /// <summary>
  131. /// Create model data from geometry and material data. Only used internally
  132. /// for the editors, it makes no sense to create content objects in the
  133. /// engine itself. You can dynamically create rendering classes like in
  134. /// this case a Mesh class with custom geometry and custom materials,
  135. /// but it is not a loaded MeshData content class then (the link to data
  136. /// will stay null in the Mesh class).
  137. /// </summary>
  138. /// <param name="setGeometry">Set geometry data</param>
  139. /// <param name="setMaterial">Set material data</param>
  140. /// <param name="setPositionOffset">Set position offset</param>
  141. public MeshData(GeometryData setGeometry, MaterialData setMaterial,
  142. Vector setPositionOffset)
  143. // This is not a full content object yet, it is empty and needs to
  144. // be saved and then loaded again to become content.
  145. : base(EmptyName, ContentType.Mesh)
  146. {
  147. if (setGeometry == null ||
  148. setMaterial == null)
  149. {
  150. throw new NullReferenceException(
  151. "To create 'MeshData' you have to provide a valid geometry and a " +
  152. "valid material!");
  153. }
  154. Geometry = setGeometry;
  155. Material = setMaterial;
  156. PositionOffset = setPositionOffset;
  157. }
  158. #endregion
  159. #region ISaveLoadBinary Members
  160. /// <summary>
  161. /// Load model data from binary data stream.
  162. /// </summary>
  163. /// <param name="reader">Binary reader of the underlying stream</param>
  164. public void Load(BinaryReader reader)
  165. {
  166. // We currently only support our version, if more versions are added,
  167. // we need to do different loading code depending on the version here.
  168. int version = reader.ReadInt32();
  169. switch (version)
  170. {
  171. case 1:
  172. case 2:
  173. case 3:
  174. Log.InvalidVersionWarning(GetType().Name + ": " + Name +
  175. ". Sorry the old versions 1-3 are not longer supported, " +
  176. "please re-upload this mesh content file!",
  177. version, VersionNumber);
  178. break;
  179. case 4:
  180. // Create and load the geometry or just reload it, we always have one
  181. if (Geometry != null)
  182. {
  183. Geometry.Load(reader);
  184. }
  185. else
  186. {
  187. Geometry = new GeometryData(reader, Reload);
  188. }
  189. PositionOffset.Load(reader);
  190. // Meshes always have a material for them
  191. Material = MaterialData.Get(reader.ReadString());
  192. // Note: Animations are not loaded here. They are linked up in the
  193. // Model class. Most meshes have no animations and models that use
  194. // animations have usually a lot of them, not just one.
  195. break;
  196. default:
  197. Log.InvalidVersionWarning(GetType().Name + ": " + Name,
  198. version, VersionNumber);
  199. return;
  200. }
  201. }
  202. /// <summary>
  203. /// Save model data, which consists of a list of geometries and materials.
  204. /// </summary>
  205. /// <param name="writer">Writer</param>
  206. public void Save(BinaryWriter writer)
  207. {
  208. writer.Write(VersionNumber);
  209. // Save the geometry
  210. Geometry.Save(writer);
  211. PositionOffset.Save(writer);
  212. // Save the material name
  213. writer.Write(Material.Name);
  214. // Note: Animation data is not stored here, see Model.Save for that!
  215. }
  216. #endregion
  217. #region ToString (Public)
  218. /// <summary>
  219. /// To string, will display the mesh name and optionally list of animations.
  220. /// </summary>
  221. /// <returns>
  222. /// A <see cref="System.String"/> that represents this mesh instance with
  223. /// the optional list of animation names.
  224. /// </returns>
  225. public override string ToString()
  226. {
  227. return GetType().GetClassName() + " " + Name +
  228. (Animations.Count > 0
  229. ? ": Animations=" + Animations.Write()
  230. : "");
  231. }
  232. #endregion
  233. #region Methods (Private)
  234. #region Load
  235. /// <summary>
  236. /// Native load method, will just load or clone the effect data.
  237. /// </summary>
  238. /// <param name="alreadyLoadedNativeData">
  239. /// The first instance that has already loaded the required content data
  240. /// of this content class or just 'null' if there is none loaded yet (or
  241. /// anymore).
  242. /// </param>
  243. protected override void Load(Content alreadyLoadedNativeData)
  244. {
  245. try
  246. {
  247. if (alreadyLoadedNativeData != null)
  248. {
  249. // Just clone all data
  250. MeshData otherData = alreadyLoadedNativeData as MeshData;
  251. Geometry = otherData.Geometry;
  252. Material = otherData.Material;
  253. PositionOffset = otherData.PositionOffset;
  254. Animations = otherData.Animations;
  255. // This object cannot be used for cloning now, but the caller (Mesh)
  256. // can set itself as the NativeClassObject and allow native cloning.
  257. return;
  258. }
  259. if (String.IsNullOrEmpty(RelativeFilePath) == false)
  260. {
  261. // Load via the ISaveLoadBinary interface methods below.
  262. FileHelper.Load(RelativeFilePath, this);
  263. }
  264. }
  265. catch (Exception ex)
  266. {
  267. Log.Warning("Failed to load mesh data from file '" +
  268. RelativeFilePath + "': " + ex);
  269. FailedToLoad = true;
  270. }
  271. // Additionally load the mesh animations (which are children content)
  272. // Initialize a new list for all the mesh children
  273. Animations.Clear();
  274. // Note: Only direct mesh children nodes are allowed, nothing else!
  275. foreach (ContentMetaData childrenData in data.Children)
  276. {
  277. // Meshes can have other sub content as well like the material data.
  278. if (childrenData.Type == ContentType.MeshAnimation)
  279. {
  280. Animations.Add(MeshAnimationData.Get(childrenData.Name));
  281. }
  282. }
  283. // If loading was skipped or failed, fill in fallback data.
  284. if (Geometry == null)
  285. {
  286. Geometry = GeometryData.DefaultBox;
  287. }
  288. if (Material == null)
  289. {
  290. Material = MaterialData.Default;
  291. }
  292. }
  293. #endregion
  294. #endregion
  295. }
  296. }