//-------------------------------------------------------------------------------------- // File: DXMUTMesh.cs // // Support code for loading DirectX .X files. // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- using System; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace Microsoft.Samples.DirectX.UtilityToolkit { /// Class for loading and rendering file-based meshes public sealed class FrameworkMesh : IDisposable { #region Instance Data private string meshFileName; private Mesh systemMemoryMesh = null; // System Memory mesh, lives through a resize private Mesh localMemoryMesh = null; // Local mesh, rebuilt on resize private Material[] meshMaterials = null; // Materials for the mesh private BaseTexture[] meshTextures = null; // Textures for the mesh private bool isUsingMeshMaterials = true; // Should the mesh be rendered with the materials /// Returns the system memory mesh public Mesh SystemMesh { get { return systemMemoryMesh; } } /// Returns the local memory mesh public Mesh LocalMesh { get { return localMemoryMesh; } } /// Should the mesh be rendered with materials public bool IsUsingMaterials{ get { return isUsingMeshMaterials; } set { isUsingMeshMaterials = value; } } /// Number of materials in mesh public int NumberMaterials { get { return meshMaterials.Length; } } /// Gets a texture from the mesh public BaseTexture GetTexture(int index) { return meshTextures[index]; } /// Gets a material from the mesh public Material GetMaterial(int index) { return meshMaterials[index]; } #endregion #region Creation /// Create a new mesh using this file public FrameworkMesh(Device device, string name) { meshFileName = name; Create(device, meshFileName); } /// Create a new mesh public FrameworkMesh() : this(null, "FrameworkMeshFile_Mesh") {} /// Create the mesh data public void Create(Device device, string name) { // Hook the device events System.Diagnostics.Debug.Assert(device != null, "Device should not be null."); device.DeviceLost += new EventHandler(OnLostDevice); device.DeviceReset += new EventHandler(OnResetDevice); device.Disposing += new EventHandler(OnDeviceDisposing); GraphicsStream adjacency; // Adjacency information ExtendedMaterial[] materials; // Mesh material information // First try to find the filename string path = string.Empty; try { path = Utility.FindMediaFile(name); } catch(MediaNotFoundException) { // The media was not found, maybe a full path was passed in? if (System.IO.File.Exists(name)) { path = name; } else { // No idea what this is trying to find throw new MediaNotFoundException(); } } // Now load the mesh systemMemoryMesh = Mesh.FromFile(path, MeshFlags.SystemMemory, device, out adjacency, out materials); using (adjacency) { // Optimize the mesh for performance systemMemoryMesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeCompact | MeshFlags.OptimizeAttributeSort, adjacency); // Find the folder of where the mesh file is located string folder = Utility.AppendDirectorySeparator(new System.IO.FileInfo(path).DirectoryName); // Create the materials CreateMaterials(folder, device, adjacency, materials); } // Finally call reset OnResetDevice(device, EventArgs.Empty); } // TODO: Create with XOF /// Create the materials for the mesh public void CreateMaterials(string folder, Device device, GraphicsStream adjacency, ExtendedMaterial[] materials) { // Does the mesh have materials? if ((materials != null) && (materials.Length > 0)) { // Allocate the arrays for the materials meshMaterials = new Material[materials.Length]; meshTextures = new BaseTexture[materials.Length]; // Copy each material and create it's texture for(int i = 0; i < materials.Length; i++) { // Copy the material first meshMaterials[i] = materials[i].Material3D; // Is there a texture for this material? if ((materials[i].TextureFilename == null) || (materials[i].TextureFilename.Length == 0) ) continue; // No, just continue now ImageInformation info = new ImageInformation(); string textureFile = folder + materials[i].TextureFilename; try { // First look for the texture in the same folder as the input folder info = TextureLoader.ImageInformationFromFile(textureFile); } catch { try { // Couldn't find it, look in the media folder textureFile = Utility.FindMediaFile(materials[i].TextureFilename); info = TextureLoader.ImageInformationFromFile(textureFile); } catch (MediaNotFoundException) { // Couldn't find it anywhere, skip it continue; } } switch (info.ResourceType) { case ResourceType.Textures: meshTextures[i] = TextureLoader.FromFile(device, textureFile); break; case ResourceType.CubeTexture: meshTextures[i] = TextureLoader.FromCubeFile(device, textureFile); break; case ResourceType.VolumeTexture: meshTextures[i] = TextureLoader.FromVolumeFile(device, textureFile); break; } } } } #endregion #region Class Methods /// Updates the mesh to a new vertex format public void SetVertexFormat(Device device, VertexFormats format) { Mesh tempSystemMesh = null; Mesh tempLocalMesh = null; VertexFormats oldFormat = VertexFormats.None; using(systemMemoryMesh) { using (localMemoryMesh) { // Clone the meshes if (systemMemoryMesh != null) { oldFormat = systemMemoryMesh.VertexFormat; tempSystemMesh = systemMemoryMesh.Clone(systemMemoryMesh.Options.Value, format, device); } if (localMemoryMesh != null) { tempLocalMesh = localMemoryMesh.Clone(localMemoryMesh.Options.Value, format, device); } } } // Store the new meshes systemMemoryMesh = tempSystemMesh; localMemoryMesh = tempLocalMesh; // Compute normals if they are being requested and the old mesh didn't have them if ( ((oldFormat & VertexFormats.Normal) == 0) && (format != 0) ) { if (systemMemoryMesh != null) systemMemoryMesh.ComputeNormals(); if (localMemoryMesh != null) localMemoryMesh.ComputeNormals(); } } /// Updates the mesh to a new vertex declaration public void SetVertexDeclaration(Device device, VertexElement[] decl) { Mesh tempSystemMesh = null; Mesh tempLocalMesh = null; VertexElement[] oldDecl = null; using(systemMemoryMesh) { using (localMemoryMesh) { // Clone the meshes if (systemMemoryMesh != null) { oldDecl = systemMemoryMesh.Declaration; tempSystemMesh = systemMemoryMesh.Clone(systemMemoryMesh.Options.Value, decl, device); } if (localMemoryMesh != null) { tempLocalMesh = localMemoryMesh.Clone(localMemoryMesh.Options.Value, decl, device); } } } // Store the new meshes systemMemoryMesh = tempSystemMesh; localMemoryMesh = tempLocalMesh; bool hadNormal = false; // Check if the old declaration contains a normal. for(int i = 0; i < oldDecl.Length; i++) { if (oldDecl[i].DeclarationUsage == DeclarationUsage.Normal) { hadNormal = true; break; } } // Check to see if the new declaration has a normal bool hasNormalNow = false; for(int i = 0; i < decl.Length; i++) { if (decl[i].DeclarationUsage == DeclarationUsage.Normal) { hasNormalNow = true; break; } } // Compute normals if they are being requested and the old mesh didn't have them if ( !hadNormal && hasNormalNow ) { if (systemMemoryMesh != null) systemMemoryMesh.ComputeNormals(); if (localMemoryMesh != null) localMemoryMesh.ComputeNormals(); } } /// Occurs after the device has been reset private void OnResetDevice(object sender, EventArgs e) { Device device = sender as Device; if (systemMemoryMesh == null) throw new InvalidOperationException("There is no system memory mesh. Nothing to do here."); // Make a local memory version of the mesh. Note: because we are passing in // no flags, the default behavior is to clone into local memory. localMemoryMesh = systemMemoryMesh.Clone((systemMemoryMesh.Options.Value & ~MeshFlags.SystemMemory), systemMemoryMesh.VertexFormat, device); } /// Occurs before the device is going to be reset private void OnLostDevice(object sender, EventArgs e) { if (localMemoryMesh != null) localMemoryMesh.Dispose(); localMemoryMesh = null; } /// Renders this mesh public void Render(Device device, bool canDrawOpaque, bool canDrawAlpha) { if (localMemoryMesh == null) throw new InvalidOperationException("No local memory mesh."); // Frist, draw the subsets without alpha if (canDrawOpaque) { for (int i = 0; i < meshMaterials.Length; i++) { if (isUsingMeshMaterials) { if (meshMaterials[i].DiffuseColor.Alpha < 1.0f) continue; // Only drawing opaque right now // set the device material and texture device.Material = meshMaterials[i]; device.SetTexture(0, meshTextures[i]); } localMemoryMesh.DrawSubset(i); } } // Then, draw the subsets with alpha if (canDrawAlpha) { for (int i = 0; i < meshMaterials.Length; i++) { if (meshMaterials[i].DiffuseColor.Alpha == 1.0f) continue; // Only drawing non-opaque right now // set the device material and texture device.Material = meshMaterials[i]; device.SetTexture(0, meshTextures[i]); localMemoryMesh.DrawSubset(i); } } } /// Renders this mesh public void Render(Device device) { Render(device, true, true); } // TODO: Render with effect /// Compute a bounding sphere for this mesh public float ComputeBoundingSphere(out Vector3 center) { if (systemMemoryMesh == null) throw new InvalidOperationException("There is no system memory mesh. Nothing to do here."); // Get the object declaration int strideSize = VertexInformation.GetFormatSize(systemMemoryMesh.VertexFormat); // Lock the vertex buffer GraphicsStream data = null; try { data = systemMemoryMesh.LockVertexBuffer(LockFlags.ReadOnly); // Now compute the bounding sphere return Geometry.ComputeBoundingSphere(data, systemMemoryMesh.NumberVertices, systemMemoryMesh.VertexFormat, out center); } finally { // Make sure to unlock the vertex buffer if (data != null) systemMemoryMesh.UnlockVertexBuffer(); } } #endregion #region IDisposable Members /// Cleans up any resources required when this object is disposed public void Dispose() { OnLostDevice(null, EventArgs.Empty); if (meshTextures != null) { for(int i = 0; i < meshTextures.Length; i++) { if (meshTextures[i] != null) meshTextures[i].Dispose(); } } meshTextures = null; meshMaterials = null; if (systemMemoryMesh != null) systemMemoryMesh.Dispose(); systemMemoryMesh = null; } /// Cleans up any resources required when this object is disposed private void OnDeviceDisposing(object sender, EventArgs e) { // Just dispose of our class Dispose(); } #endregion } }