PageRenderTime 84ms CodeModel.GetById 40ms app.highlight 10ms RepoModel.GetById 30ms app.codeStats 0ms

/ContentSystem/Rendering/MeshData.cs

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