/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