/// <summary> /// Returns a cached <see cref="XTexture"/> or creates a new one if the requested ID is not cached. /// </summary> /// <param name="engine">The <see cref="Engine"/> providing the cache and rendering capabilities.</param> /// <param name="id">The ID of the asset to be returned.</param> /// <param name="meshTexture">Shall the texture be loaded from the meshes directory instead of the textures directory?</param> /// <returns>The requested asset; <c>null</c> if <paramref name="id"/> was empty.</returns> /// <exception cref="FileNotFoundException">The specified file could not be found.</exception> /// <exception cref="IOException">There was an error reading the file.</exception> /// <exception cref="UnauthorizedAccessException">Read access to the file is not permitted.</exception> /// <exception cref="InvalidDataException">The file does not contain a valid texture.</exception> /// <remarks>Remember to call <see cref="CacheManager.Clean"/> when done, otherwise this object will never be released.</remarks> public static XTexture Get(Engine engine, string id, bool meshTexture = false) { #region Sanity checks if (engine == null) { throw new ArgumentNullException(nameof(engine)); } if (string.IsNullOrEmpty(id)) { return(null); } #endregion // Try to find existing asset in cache string type = meshTexture ? "Meshes" : "Textures"; id = FileUtils.UnifySlashes(id); string fullID = Path.Combine(type, id); var data = engine.Cache.GetAsset <XTexture>(fullID); // Load from file if not in cache if (data == null) { using (new TimedLogEvent((meshTexture ? "Loading mesh texture: " : "Loading texture: ") + id)) using (var stream = ContentManager.GetFileStream(type, id)) data = new XTexture(engine, stream) { Name = fullID }; engine.Cache.AddAsset(data); } return(data); }
//--------------------// #region Texture helpers /// <summary> /// Finds and loads a mesh texture /// </summary> /// <param name="engine">The <see cref="Engine"/> to load the texture from</param> /// <param name="meshName">The name of the mesh to load a texture file for</param> /// <param name="textureID">The texture ID</param> /// <returns>The requested mesh texture</returns> private static XTexture ShaderLoadHelper(Engine engine, string meshName, string textureID) { // Determine the path the mesh was originally loaded from string meshPath = Path.GetDirectoryName(meshName); if (!string.IsNullOrEmpty(meshPath)) { meshPath += Path.DirectorySeparatorChar; } // Try to find the texture in the directory of its mesh first, then look in the generic mesh textures directory string id = Path.Combine("Meshes", textureID); return(ContentManager.FileExists("Meshes", meshPath + textureID) ? XTexture.Get(engine, meshPath + textureID, meshTexture: true) : XTexture.Get(engine, id)); }
/// <summary> /// Loads a static mesh from an .X file. /// </summary> /// <param name="engine">The <see cref="Engine"/> providing rendering capabilities.</param> /// <param name="stream">The .X file to load the mesh from.</param> /// <param name="meshName">The name of the mesh. This is used for finding associated textures.</param> /// <exception cref="InvalidDataException"><paramref name="stream"/> does not contain a valid mesh.</exception> /// <remarks>This should only be called by <see cref="Get"/> to prevent unnecessary duplicates.</remarks> protected XMesh(Engine engine, Stream stream, string meshName) { #region Sanity checks if (engine == null) { throw new ArgumentNullException(nameof(engine)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } #endregion // Load mesh and materials try { _mesh = Mesh.FromStream(engine.Device, stream, MeshFlags.Managed); } #region Sanity checks catch (Direct3D9Exception ex) { throw new InvalidDataException(ex.Message, ex); } #endregion ExtendedMaterial[] extendedMaterials = Mesh.GetMaterials(); EffectInstance[] effectInstances = Mesh.GetEffects(); // Calculate bounding bodies before manipulating the mesh BoundingSphere = BufferHelper.ComputeBoundingSphere(Mesh); BoundingBox = BufferHelper.ComputeBoundingBox(Mesh); try { #region Extract material data bool needsTangents = false; if ((extendedMaterials != null) && (extendedMaterials.Length > 0)) { Materials = new XMaterial[extendedMaterials.Length]; // Store each material and texture for (int i = 0; i < extendedMaterials.Length; i++) { Materials[i] = XMaterial.DefaultMaterial; // Apply the mesh's material information Material meshMaterial = extendedMaterials[i].MaterialD3D; Materials[i].Diffuse = meshMaterial.Diffuse.ToColor(); Materials[i].Specular = meshMaterial.Specular.ToColor(); Materials[i].SpecularPower = meshMaterial.Power; Materials[i].Emissive = meshMaterial.Emissive.ToColor(); // Search for texture file names in material string textureFilename = extendedMaterials[i].TextureFileName; if (!string.IsNullOrEmpty(textureFilename)) { Materials[i].DiffuseMaps[0] = ShaderLoadHelper(engine, meshName, textureFilename); #region Auto-detect extra texture maps string baseFilename = Path.Combine(Path.GetDirectoryName(meshName) ?? "", Path.GetFileNameWithoutExtension(textureFilename)); string fileExt = Path.GetExtension(textureFilename); // Normal map string normalFilename = baseFilename + "_normal" + fileExt; if (ContentManager.FileExists("Meshes", normalFilename)) { Materials[i].NormalMap = XTexture.Get(engine, normalFilename, meshTexture: true); needsTangents = true; } // Height map string heightFilename = baseFilename + "_height" + fileExt; if (ContentManager.FileExists("Meshes", heightFilename)) { Materials[i].HeightMap = XTexture.Get(engine, heightFilename, meshTexture: true); needsTangents = true; } // Specular map string specularFilename = baseFilename + "_specular" + fileExt; if (ContentManager.FileExists("Meshes", specularFilename)) { Materials[i].SpecularMap = XTexture.Get(engine, specularFilename, meshTexture: true); } // Glow map (internally represented as emissive map) string glowFilename = baseFilename + "_glow" + fileExt; if (ContentManager.FileExists("Meshes", glowFilename)) { Materials[i].EmissiveMap = XTexture.Get(engine, glowFilename, meshTexture: true); // Automatically set emissive color if a glow map is used (since this color usually defaults to black) if (Materials[i].Emissive == Color.FromArgb(255, 0, 0, 0)) { Materials[i].Emissive = Color.White; } } #endregion } #region Load extra texture maps from shader configuration // Search for texture file names in shader effect if present if ((effectInstances != null) && (i < effectInstances.Length)) { EffectDefault[] parameters = effectInstances[i].Defaults; foreach (EffectDefault param in parameters) { XTexture extraTexture = ShaderTextureHelper(engine, param, meshName, "diffuseTexture"); if (extraTexture != null) { Materials[i].DiffuseMaps[0] = extraTexture; } extraTexture = ShaderTextureHelper(engine, param, meshName, "normalTexture"); if (extraTexture != null) { Materials[i].NormalMap = extraTexture; needsTangents = true; } extraTexture = ShaderTextureHelper(engine, param, meshName, "heightTexture"); if (extraTexture != null) { Materials[i].HeightMap = extraTexture; needsTangents = true; } extraTexture = ShaderTextureHelper(engine, param, meshName, "specularTexture"); if (extraTexture != null) { Materials[i].SpecularMap = extraTexture; } } } #endregion } // Generate normals (plus tagents if normal/height maps are available) if (needsTangents && engine.Capabilities.PerPixelEffects) { MeshHelper.GenerateNormalsAndTangents(engine.Device, ref _mesh, true); } else { MeshHelper.GenerateNormals(engine.Device, ref _mesh); } } #endregion } #region Error handling catch (Exception) { // Since private objects have already been created at this point, a proper cleanup is needed Dispose(); throw; } #endregion }