/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
- using System;
- using System.Collections.Generic;
- using System.IO;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.ContentSystem.Rendering
- {
- /// <summary>
- /// Model data for imported 3D models (from FBX, Collada, 3DS, OBJ, or DXF
- /// files). Contains a list of geometries for each mesh in the file (often
- /// just one mesh, but can be more complex) and a list of materials used
- /// in those geometry meshes.
- /// </summary>
- public class MeshData : Content, ISaveLoadBinary
- {
- #region Constants
- /// <summary>
- /// Version number for this ModelData. If this goes above 1, we need
- /// to support loading older versions as well. Saving is always going
- /// to be the latest version (this one). Version 1, 2 and 3 were used
- /// early in 2011 for combined meshes with material and animation data
- /// baked into it. Now with version 4 and higher for v0.8.7 and up meshes
- /// only contain geometry, bones, offset and a link to the used material
- /// content file (which contains the textures and shaders used).
- /// </summary>
- private const int VersionNumber = 4;
- #endregion
-
- #region Get (Static)
- /// <summary>
- /// Get and load content based on the content name. This method makes sure
- /// we do not load the same content twice (the constructor is protected).
- /// </summary>
- /// <param name="contentName">Content name we want to load, this is
- /// passed onto the Content System, which will do the actual loading with
- /// help of the Load method in this class.</param>
- /// <returns>The loaded Content object, always unique for the same
- /// name, this helps comparing data.</returns>
- public static MeshData Get(string contentName)
- {
- return Get<MeshData>(contentName, ContentType.Mesh);
- }
- #endregion
-
- #region GetAllContentNames (Static)
- /// <summary>
- /// Get list of all mesh content names available in the current project.
- /// </summary>
- /// <returns>List of all mesh content names</returns>
- public static string[] GetAllContentNames()
- {
- Dictionary<string, ContentMetaData> meshContents =
- ContentManager.GetAllContentMetaData(ContentType.Mesh);
- return ArrayHelper.ToArray(meshContents.Keys);
- }
- #endregion
-
- #region Geometry (Public)
- /// <summary>
- /// Geometries used in this model. Usually we just have one mesh geometry.
- /// Note: One difference from 3D Studio is that a Geometry mesh in our
- /// engine is only allowed to be used by one material with one shader,
- /// if a multi-material mesh was exported, it will be split up into
- /// several Geometries with different MaterialIds (see Materials).
- /// </summary>
- public GeometryData Geometry
- {
- get;
- private set;
- }
- #endregion
-
- #region Material (Public)
- /// <summary>
- /// Materials used in the Geometries above. Often we just have one
- /// material. Please note that we optimize the list of materials and
- /// geometries, no duplicates are allowed. This list is even used for
- /// levels where many meshes with the same material are merged together.
- /// A material is the same if all values are equal, which can easily
- /// happen because of texture atlas optimizations merging many textures.
- /// </summary>
- public MaterialData Material
- {
- get;
- private set;
- }
- #endregion
-
- #region HasBones (Public)
- /// <summary>
- /// Has bone data for animations? Note: The animations are in MeshAnimation.
- /// </summary>
- public bool HasBones
- {
- get
- {
- // Just check if the geometry has bones
- return Geometry.Bones.Length > 0;
- }
- }
- #endregion
-
- #region PositionOffset (Public)
- /// <summary>
- /// The offset translation of the mesh to represent its local space.
- /// This is both important for optimizations and fixing not-centered meshes
- /// and also good for rendering out meshes optimized and allowing merging
- /// easily if needed.
- /// </summary>
- public Vector PositionOffset;
- #endregion
-
- #region Animations (Public)
- /// <summary>
- /// Mesh Animation data for each specific mesh. Note: This is not loaded or
- /// set here when loading content. Animations are always loaded and set by
- /// the Model class (but you can do whatever you want in your own code).
- /// Also notice that setting the active Animation and switching between
- /// them all happens in the Model class as well.
- /// <para />
- /// Note: Using the same MeshData with different Models using it with
- /// different animations is currently not supported and might cause
- /// trouble.
- /// </summary>
- public List<MeshAnimationData> Animations = new List<MeshAnimationData>();
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create mesh data from a model content name, use Get to call this.
- /// </summary>
- /// <param name="meshContentName">Name of the mesh content.</param>
- protected MeshData(string meshContentName)
- : base(meshContentName, ContentType.Mesh)
- {
- }
-
- /// <summary>
- /// Create model data from geometry and material data. Only used internally
- /// for the editors, it makes no sense to create content objects in the
- /// engine itself. You can dynamically create rendering classes like in
- /// this case a Mesh class with custom geometry and custom materials,
- /// but it is not a loaded MeshData content class then (the link to data
- /// will stay null in the Mesh class).
- /// </summary>
- /// <param name="setGeometry">Set geometry data</param>
- /// <param name="setMaterial">Set material data</param>
- /// <param name="setPositionOffset">Set position offset</param>
- public MeshData(GeometryData setGeometry, MaterialData setMaterial,
- Vector setPositionOffset)
- // This is not a full content object yet, it is empty and needs to
- // be saved and then loaded again to become content.
- : base(EmptyName, ContentType.Mesh)
- {
- if (setGeometry == null ||
- setMaterial == null)
- {
- throw new NullReferenceException(
- "To create 'MeshData' you have to provide a valid geometry and a " +
- "valid material!");
- }
-
- Geometry = setGeometry;
- Material = setMaterial;
- PositionOffset = setPositionOffset;
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Load model data from binary data stream.
- /// </summary>
- /// <param name="reader">Binary reader of the underlying stream</param>
- public void Load(BinaryReader reader)
- {
- // We currently only support our version, if more versions are added,
- // we need to do different loading code depending on the version here.
- int version = reader.ReadInt32();
- switch (version)
- {
- case 1:
- case 2:
- case 3:
- Log.InvalidVersionWarning(GetType().Name + ": " + Name +
- ". Sorry the old versions 1-3 are not longer supported, " +
- "please re-upload this mesh content file!",
- version, VersionNumber);
- break;
-
- case 4:
- // Create and load the geometry or just reload it, we always have one
- if (Geometry != null)
- {
- Geometry.Load(reader);
- }
- else
- {
- Geometry = new GeometryData(reader, Reload);
- }
- PositionOffset.Load(reader);
- // Meshes always have a material for them
- Material = MaterialData.Get(reader.ReadString());
- // Note: Animations are not loaded here. They are linked up in the
- // Model class. Most meshes have no animations and models that use
- // animations have usually a lot of them, not just one.
- break;
-
- default:
- Log.InvalidVersionWarning(GetType().Name + ": " + Name,
- version, VersionNumber);
- return;
- }
- }
-
- /// <summary>
- /// Save model data, which consists of a list of geometries and materials.
- /// </summary>
- /// <param name="writer">Writer</param>
- public void Save(BinaryWriter writer)
- {
- writer.Write(VersionNumber);
- // Save the geometry
- Geometry.Save(writer);
- PositionOffset.Save(writer);
- // Save the material name
- writer.Write(Material.Name);
- // Note: Animation data is not stored here, see Model.Save for that!
- }
- #endregion
-
- #region ToString (Public)
- /// <summary>
- /// To string, will display the mesh name and optionally list of animations.
- /// </summary>
- /// <returns>
- /// A <see cref="System.String"/> that represents this mesh instance with
- /// the optional list of animation names.
- /// </returns>
- public override string ToString()
- {
- return GetType().GetClassName() + " " + Name +
- (Animations.Count > 0
- ? ": Animations=" + Animations.Write()
- : "");
- }
- #endregion
-
- #region Methods (Private)
-
- #region Load
- /// <summary>
- /// Native load method, will just load or clone the effect data.
- /// </summary>
- /// <param name="alreadyLoadedNativeData">
- /// The first instance that has already loaded the required content data
- /// of this content class or just 'null' if there is none loaded yet (or
- /// anymore).
- /// </param>
- protected override void Load(Content alreadyLoadedNativeData)
- {
- try
- {
- if (alreadyLoadedNativeData != null)
- {
- // Just clone all data
- MeshData otherData = alreadyLoadedNativeData as MeshData;
- Geometry = otherData.Geometry;
- Material = otherData.Material;
- PositionOffset = otherData.PositionOffset;
- Animations = otherData.Animations;
- // This object cannot be used for cloning now, but the caller (Mesh)
- // can set itself as the NativeClassObject and allow native cloning.
- return;
- }
-
- if (String.IsNullOrEmpty(RelativeFilePath) == false)
- {
- // Load via the ISaveLoadBinary interface methods below.
- FileHelper.Load(RelativeFilePath, this);
- }
- }
- catch (Exception ex)
- {
- Log.Warning("Failed to load mesh data from file '" +
- RelativeFilePath + "': " + ex);
- FailedToLoad = true;
- }
-
- // Additionally load the mesh animations (which are children content)
- // Initialize a new list for all the mesh children
- Animations.Clear();
- // Note: Only direct mesh children nodes are allowed, nothing else!
- foreach (ContentMetaData childrenData in data.Children)
- {
- // Meshes can have other sub content as well like the material data.
- if (childrenData.Type == ContentType.MeshAnimation)
- {
- Animations.Add(MeshAnimationData.Get(childrenData.Name));
- }
- }
-
- // If loading was skipped or failed, fill in fallback data.
- if (Geometry == null)
- {
- Geometry = GeometryData.DefaultBox;
- }
- if (Material == null)
- {
- Material = MaterialData.Default;
- }
- }
- #endregion
-
- #endregion
- }
- }
-