private static void MergeAnimation(string animationFile, AnimationContentDictionary animationDictionary, ContentIdentity contentIdentity, ContentProcessorContext context) { if (string.IsNullOrEmpty(animationFile)) { return; } if (animationDictionary == null) { throw new ArgumentNullException("animationDictionary"); } if (context == null) { throw new ArgumentNullException("context"); } // Use content pipeline to build the asset. animationFile = ContentHelper.FindFile(animationFile, contentIdentity); NodeContent mergeModel = context.BuildAndLoadAsset <NodeContent, NodeContent>(new ExternalReference <NodeContent>(animationFile), null); // Find the skeleton. BoneContent mergeRoot = MeshHelper.FindSkeleton(mergeModel); if (mergeRoot == null) { context.Logger.LogWarning(null, contentIdentity, "Animation model file '{0}' has no root bone. Cannot merge animations.", animationFile); return; } // Merge all animations of the skeleton root node. foreach (string animationName in mergeRoot.Animations.Keys) { if (animationDictionary.ContainsKey(animationName)) { context.Logger.LogWarning(null, contentIdentity, "Replacing animation '{0}' with merged animation from '{1}'.", animationName, animationFile); animationDictionary[animationName] = mergeRoot.Animations[animationName]; } else { context.Logger.LogImportantMessage("Merging animation '{0}' from '{1}'.", animationName, animationFile); animationDictionary.Add(animationName, mergeRoot.Animations[animationName]); } } }
/// <summary> /// Called by the XNA Framework when importing an texture file to be used as a game asset. This /// is the method called by the XNA Framework when an asset is to be imported into an object /// that can be recognized by the Content Pipeline. /// </summary> /// <param name="filename">Name of a game asset file.</param> /// <param name="context"> /// Contains information for importing a game asset, such as a logger interface. /// </param> /// <returns>Resulting game asset.</returns> public override TextureContent Import(string filename, ContentImporterContext context) { string extension = Path.GetExtension(filename); if (extension != null) { Texture texture = null; if (extension.Equals(".DDS", StringComparison.OrdinalIgnoreCase)) { using (var stream = File.OpenRead(filename)) texture = DdsHelper.Load(stream, DdsFlags.ForceRgb | DdsFlags.ExpandLuminance); } else if (extension.Equals(".TGA", StringComparison.OrdinalIgnoreCase)) { using (var stream = File.OpenRead(filename)) texture = TgaHelper.Load(stream); } if (texture != null) { #if !MONOGAME // When using the XNA content pipeline, check for MonoGame content. if (!string.IsNullOrEmpty(ContentHelper.GetMonoGamePlatform())) #endif { // These formats are not (yet) available in MonoGame. switch (texture.Description.Format) { case DataFormat.B5G5R5A1_UNORM: // (16-bit TGA files.) case DataFormat.R8_UNORM: case DataFormat.A8_UNORM: texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); break; } } // Convert DigitalRune Texture to XNA TextureContent. var identity = new ContentIdentity(filename, "DigitalRune"); return(TextureHelper.ToContent(texture, identity)); } } return(base.Import(filename, context)); }
private static void SetSkinnedMaterial(NodeContent node, Dictionary <MaterialContent, SkinnedMaterialContent> swappedMaterials, SkinnedMaterialContent defaultMaterial) { var mesh = node as MeshContent; if (mesh != null) { foreach (var geometry in mesh.Geometry) { if (!ContentHelper.IsSkinned(geometry)) { continue; } if (geometry.Material == null) { // Happens if the model is exported without a material. (XNA only!) geometry.Material = defaultMaterial; continue; } SkinnedMaterialContent skinnedMaterial; if (!swappedMaterials.TryGetValue(geometry.Material, out skinnedMaterial)) { // Convert BasicMaterialContent to SkinnedMaterialContent. skinnedMaterial = ContentHelper.ConvertToSkinnedMaterial(geometry.Material); if (skinnedMaterial == null) { continue; } swappedMaterials[geometry.Material] = skinnedMaterial; } geometry.Material = skinnedMaterial; } } foreach (var child in node.Children) { SetSkinnedMaterial(child, swappedMaterials, defaultMaterial); } }
/// <summary> /// Imports the asset. /// </summary> /// <param name="context">Contains any required custom process parameters.</param> public void Import(ContentProcessorContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (string.IsNullOrWhiteSpace(ModelDescription.FileName)) { throw new InvalidContentException("The attribute 'File' is not set in the model description (.drmdl file).", Identity); } var fileName = ContentHelper.FindFile(ModelDescription.FileName, Identity); var asset = new ExternalReference <NodeContent>(fileName); var node = context.BuildAndLoadAsset <NodeContent, NodeContent>(asset, null, null, ModelDescription.Importer); // BuildAndLoadAsset does not return root node in MonoGame. while (node.Parent != null) { node = node.Parent; } if (node.GetType() == typeof(NodeContent)) { // Root node is of type NodeContent. // --> Copy root node content and children. Name = node.Name; Transform = node.Transform; Animations.AddRange(node.Animations); OpaqueData.AddRange(node.OpaqueData); var children = node.Children.ToArray(); node.Children.Clear(); // Clear parents. Children.AddRange(children); } else { // Root node is a derived type. // --> Add node as child. Children.Add(node); } }
private static void ValidateOpaqueData(ContentItem input) { foreach (var data in input.OpaqueData) { if (data.Key == "Effect" || data.Key == "CompiledEffect") { // "Effect" and "CompiledEffect" have already been handled. continue; } // All other opaque data only make sense if the can be used as effect parameter // value. Only certain types can be used for effect parameters. if (!ContentHelper.IsValidTypeForEffectParameter(data.Value)) { string message = string.Format( CultureInfo.InvariantCulture, "Material pass \"{0}\" contains invalid type for effect parameter. Name = \"{1}\", Type = \"{2}\".", input.Name, data.Key, data.Value.GetType()); throw new InvalidContentException(message, input.Identity); } } }
/// <summary> /// Bakes all node transforms in the specified subtree into the mesh geometry so that each /// node's transform is Identity. (Only bones and morph targets keep their transforms.) /// </summary> /// <param name="node">The node.</param> private static void BakeAllTransforms(NodeContent node) { if (node is BoneContent) { return; } if (ContentHelper.IsMorphTarget(node)) { return; } if (node.Transform != Matrix.Identity) { MeshHelper.TransformScene(node, node.Transform); node.Transform = Matrix.Identity; } foreach (NodeContent child in node.Children) { BakeAllTransforms(child); } }
/// <summary> /// Builds the material. /// </summary> /// <param name="material"> /// The external material (<see cref="string"/>) or the local material /// (<see cref="MaterialContent"/>). /// </param> /// <returns> /// The processed material. /// </returns> private object BuildMaterial(object material) { object convertedMaterial; if (!_materials.TryGetValue(material, out convertedMaterial)) { string name = material as string; if (name != null) { // Build external material (XML file). string fileName = ContentHelper.FindFile(name, _input.Identity); var reference = new ExternalReference <DRMaterialContent>(fileName); convertedMaterial = OnBuildMaterial(reference, _context); } else { // Build local material. convertedMaterial = OnConvertMaterial((MaterialContent)material, _context); } } return(convertedMaterial); }
private DRMaterialContent ProcessInternal(MaterialContent input, ContentProcessorContext context) { // Create effect binding for default render pass. var binding = new DREffectBindingContent { Name = DefaultPass, Identity = input.Identity }; // Copy opaque data from material content. ValidateOpaqueData(input); foreach (var entry in input.OpaqueData) { binding.OpaqueData.Add(entry.Key, entry.Value); } foreach (var entry in input.Textures) { binding.Textures.Add(entry.Key, entry.Value); } // ----- Effect if (input is AlphaTestMaterialContent) { binding.EffectType = DREffectType.AlphaTestEffect; } else if (input is DualTextureMaterialContent) { binding.EffectType = DREffectType.DualTextureEffect; } else if (input is EnvironmentMapMaterialContent) { binding.EffectType = DREffectType.EnvironmentMapEffect; } else if (input is SkinnedMaterialContent) { binding.EffectType = DREffectType.SkinnedEffect; } else { var effectMaterial = input as EffectMaterialContent; if (effectMaterial == null || effectMaterial.Effect == null || string.IsNullOrEmpty(effectMaterial.Effect.Filename)) { // The material is either // - a BasicMaterialContent (default) // - or an EffectMaterialContent without an effect. // --> Use the DefaultEffect. binding.EffectType = DefaultEffectType; if (DefaultEffectType == DREffectType.CustomEffect) { if (String.IsNullOrEmpty(DefaultEffectFile)) { throw new InvalidContentException("DefaultEffectType is set to CustomEffect, but DefaultEffectFile is null or empty.", input.Identity); } string fileName = ContentHelper.FindFile(DefaultEffectFile, input.Identity); binding.Effect = new ExternalReference <EffectContent>(fileName); } } } ProcessEffect(binding, context); // ----- Textures foreach (var texture in binding.Textures.Values) { // Store texture parameters in opaque data. texture.OpaqueData.Clear(); texture.OpaqueData.Add("ColorKeyColor", ColorKeyColor); texture.OpaqueData.Add("ColorKeyEnabled", ColorKeyEnabled); texture.OpaqueData.Add("GenerateMipmaps", GenerateMipmaps); texture.OpaqueData.Add("InputGamma", InputTextureGamma); texture.OpaqueData.Add("OutputGamma", OutputTextureGamma); texture.OpaqueData.Add("PremultiplyAlpha", PremultiplyTextureAlpha); texture.OpaqueData.Add("ResizeToPowerOfTwo", ResizeTexturesToPowerOfTwo); texture.OpaqueData.Add("Format", TextureFormat); texture.OpaqueData.Add("ReferenceAlpha", ReferenceAlpha); texture.OpaqueData.Add("ScaleAlphaToCoverage", ScaleTextureAlphaToCoverage); } ProcessTextures(binding, context, input.Identity); // Create DigitalRune material with default render pass. return(new DRMaterialContent { Name = input.Name, Identity = input.Identity, Passes = new Dictionary <string, DREffectBindingContent> { { DefaultPass, binding } } }); }
private DRMaterialContent ProcessInternal(DRMaterialContent material, ContentProcessorContext context) { material.Passes = new Dictionary <string, DREffectBindingContent>(); // Parse XML file. var document = material.Definition; var identity = material.Identity; var materialElement = document.Root; if (materialElement == null || materialElement.Name != "Material") { string message = string.Format(CultureInfo.InvariantCulture, "Root element \"<Material>\" is missing in XML."); throw new InvalidContentException(message, identity); } // Override material name, if attribute is set. material.Name = (string)materialElement.Attribute("Name") ?? material.Name; // Create effect bindings for all render passes. foreach (var passElement in materialElement.Elements("Pass")) { string pass = passElement.GetMandatoryAttribute("Name", identity); if (material.Passes.ContainsKey(pass)) { string message = XmlHelper.GetExceptionMessage(passElement, "Duplicate entry. The pass \"{0}\" was already defined.", pass); throw new InvalidContentException(message, identity); } var binding = new DREffectBindingContent { Name = pass, Identity = identity }; // Skip this pass if the graphics profile does not match the actual target profile. string profile = (string)passElement.Attribute("Profile") ?? "ANY"; string profileLower = profile.ToUpperInvariant(); if (profileLower == "REACH") { if (context.TargetProfile != GraphicsProfile.Reach) { continue; } } else if (profileLower == "HIDEF") { if (context.TargetProfile != GraphicsProfile.HiDef) { continue; } } else if (profileLower != "ANY") { string message = XmlHelper.GetExceptionMessage(passElement, "Unknown profile: \"{0}\". Allowed values are \"Reach\", \"HiDef\" or \"Any\"", profile); throw new InvalidContentException(message, identity); } // ----- Effect string effectName = passElement.GetMandatoryAttribute("Effect", identity); switch (effectName) { case "AlphaTestEffect": binding.EffectType = DREffectType.AlphaTestEffect; break; case "BasicEffect": binding.EffectType = DREffectType.BasicEffect; break; case "DualTextureEffect": binding.EffectType = DREffectType.DualTextureEffect; break; case "EnvironmentMapEffect": binding.EffectType = DREffectType.EnvironmentMapEffect; break; case "SkinnedEffect": binding.EffectType = DREffectType.SkinnedEffect; break; default: binding.EffectType = DREffectType.CustomEffect; if (string.IsNullOrEmpty(Path.GetExtension(effectName))) { // The effect is a prebuilt asset. effectName is the name of the asset, // for example: effectName = "DigitalRune\GBuffer". binding.EffectAsset = effectName; } else { // The effect is a local .fx file. effectName = ContentHelper.FindFile(effectName, identity); binding.Effect = new ExternalReference <EffectContent>(effectName); } break; } ProcessEffect(binding, context); // ----- Parameters foreach (var parameterElement in passElement.Elements("Parameter")) { string name = parameterElement.GetMandatoryAttribute("Name", identity); if (binding.OpaqueData.ContainsKey(name)) { string message = XmlHelper.GetExceptionMessage(parameterElement, "Duplicate entry. The parameter \"{0}\" was already defined.", name); throw new InvalidContentException(message, identity); } object value = parameterElement.Attribute("Value").ToParameterValue(null, identity); if (value != null) { binding.OpaqueData.Add(name, value); } } // ----- Textures foreach (var textureElement in passElement.Elements("Texture")) { string name = textureElement.GetMandatoryAttribute("Name", identity); if (binding.Textures.ContainsKey(name)) { string message = XmlHelper.GetExceptionMessage(textureElement, "Duplicate entry. The texture \"{0}\" was already defined.", name); throw new InvalidContentException(message, identity); } string fileName = textureElement.GetMandatoryAttribute("File", identity); fileName = ContentHelper.FindFile(fileName, identity); // Texture processor parameters. var colorKeyAttribute = textureElement.Attribute("ColorKey"); bool colorKeyEnabled = colorKeyAttribute != null; Color colorKeyColor = colorKeyAttribute.ToColor(Color.Magenta, identity); bool generateMipmaps = (bool?)textureElement.Attribute("GenerateMipmaps") ?? true; float inputGamma = (float?)textureElement.Attribute("InputGamma") ?? 2.2f; float outputGamma = (float?)textureElement.Attribute("OutputGamma") ?? 2.2f; bool premultiplyAlpha = (bool?)textureElement.Attribute("PremultiplyAlpha") ?? true; bool resizeToPowerOfTwo = (bool?)textureElement.Attribute("ResizeToPowerOfTwo") ?? false; DRTextureFormat textureFormat = textureElement.Attribute("Format").ToTextureFormat(DRTextureFormat.Dxt, identity); float referenceAlpha = (float?)textureElement.Attribute("ReferenceAlpha") ?? 0.9f; bool scaleAlphaToCoverage = (bool?)textureElement.Attribute("ScaleAlphaToCoverage") ?? false; // Store texture parameters in opaque data. var texture = new ExternalReference <TextureContent>(fileName); var defaultTextureProcessor = new DRTextureProcessor(); if (colorKeyColor != defaultTextureProcessor.ColorKeyColor) { texture.OpaqueData.Add("ColorKeyColor", colorKeyColor); } if (colorKeyEnabled != defaultTextureProcessor.ColorKeyEnabled) { texture.OpaqueData.Add("ColorKeyEnabled", colorKeyEnabled); } if (generateMipmaps != defaultTextureProcessor.GenerateMipmaps) { texture.OpaqueData.Add("GenerateMipmaps", generateMipmaps); } if (inputGamma != defaultTextureProcessor.InputGamma) { texture.OpaqueData.Add("InputGamma", inputGamma); } if (outputGamma != defaultTextureProcessor.OutputGamma) { texture.OpaqueData.Add("OutputGamma", outputGamma); } if (premultiplyAlpha != defaultTextureProcessor.PremultiplyAlpha) { texture.OpaqueData.Add("PremultiplyAlpha", premultiplyAlpha); } if (resizeToPowerOfTwo != defaultTextureProcessor.ResizeToPowerOfTwo) { texture.OpaqueData.Add("ResizeToPowerOfTwo", resizeToPowerOfTwo); } if (textureFormat != defaultTextureProcessor.Format) { texture.OpaqueData.Add("Format", textureFormat); } if (referenceAlpha != defaultTextureProcessor.ReferenceAlpha) { texture.OpaqueData.Add("ReferenceAlpha", referenceAlpha); } if (scaleAlphaToCoverage != defaultTextureProcessor.ScaleAlphaToCoverage) { texture.OpaqueData.Add("ScaleAlphaToCoverage", scaleAlphaToCoverage); } binding.Textures.Add(name, texture); } ProcessTextures(binding, context, identity); material.Passes.Add(pass, binding); } return(material); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- /// <summary> /// Traverses the scene graph and collapses all LODs into <strong>LodGroupNodes</strong>. /// </summary> private void CombineLodGroups() { // The LOD level is encoded in the scene node name. Example: "MeshXyz_LOD2" // --> Collect all LOD levels. var lodGroupNodes = new Dictionary <string, DRLodGroupNodeContent>(); foreach (var node in _model.GetSubtree()) { string name; int? lodLevel; ContentHelper.ParseSceneNodeName(node.Name, out name, out lodLevel); if (lodLevel.HasValue && !string.IsNullOrWhiteSpace(name)) { // Scene node is part of LOD group. DRLodGroupNodeContent lodGroupNode; if (!lodGroupNodes.TryGetValue(name, out lodGroupNode)) { lodGroupNode = new DRLodGroupNodeContent { Name = name }; lodGroupNodes.Add(name, lodGroupNode); } lodGroupNode.Levels.Add(node); node.LodLevel = lodLevel.Value; } } // Detach scene nodes that belong to LOD group and replace them with a single LodGroupNode. foreach (var lodGroupNode in lodGroupNodes.Values) { // Find LOD0 (= the scene node that represents the highest level of detail). var lod0 = lodGroupNode.Levels.FirstOrDefault(n => n.LodLevel == 0); if (lod0 == null) { // LOD0 is not in list. The scene node does probably not have "_LOD0" in its name. // --> Search for scene node without extension "_LOD0". lod0 = _model.GetSubtree().FirstOrDefault(n => n.Name == lodGroupNode.Name); if (lod0 != null) { lodGroupNode.Levels.Add(lod0); lod0.LodLevel = 0; } } // Note: At this point all LODs should be registered in LOD group. lodGroupNode.Levels.Sort(LodComparer.Instance); // Set MaxDistance by checking all LOD nodes. lodGroupNode.MaxDistance = lodGroupNode.Levels .SelectMany(n => n.GetSubtree()) .Max(n => n.MaxDistance); if (lod0 == null) { // LOD0 was not found. // --> As a fallback use first LOD. lod0 = lodGroupNode.Levels[0]; } // Replace LOD0 with LodGroupNode. lodGroupNode.ScaleLocal = lod0.ScaleLocal; lodGroupNode.PoseLocal = lod0.PoseLocal; // We have to set PoseWorld too. PoseWorld/PoseLocal are separate properties in // DRSceneNodeContent, unlike SceneNode where they update each other. lodGroupNode.PoseWorld = lod0.PoseWorld; lod0.Parent.Children.Add(lodGroupNode); // Remove all LODs from scene graph. foreach (var lodNode in lodGroupNode.Levels) { lodNode.Parent.Children.Remove(lodNode); } } }
private static DRSceneNodeContent BuildSceneGraph(NodeContent node, DRSceneNodeContent parent) { CheckForCyclicReferences(node); DRSceneNodeContent sceneNode; if (node is BoneContent) { // ----- BoneContent // Bones do not show up in the scene graph. sceneNode = null; } else if (node is MeshContent) { // ----- MeshContent var mesh = (MeshContent)node; string morphTargetName; if (ContentHelper.IsMorphTarget(mesh, out morphTargetName)) { // ----- Morph Targets // Morph targets are stored in the parent mesh, they do not show up in // the scene graph. Children of morph targets are ignored! mesh.Name = morphTargetName; AddMorphTarget(parent, mesh); sceneNode = null; } else if (ContentHelper.IsOccluder(mesh)) { // ----- OccluderNode sceneNode = new DROccluderNodeContent { InputMesh = mesh }; } else { // ----- MeshNode sceneNode = new DRMeshNodeContent { InputMesh = mesh }; } } else { // ----- Empty/unsupported node sceneNode = new DRSceneNodeContent(); } if (sceneNode != null) { sceneNode.Name = node.Name; Pose pose; Vector3F scale; DecomposeTransform(node, out pose, out scale); sceneNode.PoseLocal = pose; sceneNode.ScaleLocal = scale; if (node.Children.Count > 0) { // Process children. sceneNode.Children = new List <DRSceneNodeContent>(); // Recursively add children. foreach (var childNode in node.Children) { var childSceneNode = BuildSceneGraph(childNode, sceneNode); if (childSceneNode != null) { childSceneNode.Parent = sceneNode; sceneNode.Children.Add(childSceneNode); } } } } return(sceneNode); }
public static void Split(AnimationContentDictionary animationDictionary, string splitFile, ContentIdentity contentIdentity, ContentProcessorContext context) { if (animationDictionary == null) { return; } if (string.IsNullOrEmpty(splitFile)) { return; } if (contentIdentity == null) { throw new ArgumentNullException("contentIdentity"); } if (context == null) { throw new ArgumentNullException("context"); } if (animationDictionary.Count == 0) { context.Logger.LogWarning(null, contentIdentity, "The model does not have an animation. Animation splitting is skipped."); return; } if (animationDictionary.Count > 1) { context.Logger.LogWarning(null, contentIdentity, "The model contains more than 1 animation. The animation splitting is performed on the first animation. Other animations are deleted!"); } // Load XML file. splitFile = ContentHelper.FindFile(splitFile, contentIdentity); XDocument document = XDocument.Load(splitFile, LoadOptions.SetLineInfo); // Let the content pipeline know that we depend on this file and we need to // rebuild the content if the file is modified. context.AddDependency(splitFile); // Parse XML. var animationsElement = document.Element("Animations"); if (animationsElement == null) { context.Logger.LogWarning(null, contentIdentity, "The animation split file \"{0}\" does not contain an <Animations> root node.", splitFile); return; } var wrappedContext = new ContentPipelineContext(context); var splits = ParseAnimationSplitDefinitions(animationsElement, contentIdentity, wrappedContext); if (splits == null || splits.Count == 0) { context.Logger.LogWarning(null, contentIdentity, "The XML file with the animation split definitions is invalid or empty. Animation is not split."); return; } // Split animations. Split(animationDictionary, splits, contentIdentity, context); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- /// <summary> /// Processes a texture. /// </summary> /// <param name="input">The texture content to process.</param> /// <param name="context">Context for the specified processor.</param> /// <returns>The converted texture content.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="input"/> or <paramref name="context"/> is <see langword="null"/>. /// </exception> public override TextureContent Process(TextureContent input, ContentProcessorContext context) { if (input == null) { throw new ArgumentNullException("input"); } if (context == null) { throw new ArgumentNullException("context"); } // Linear vs. sRGB: // XNA does not support _SRGB texture formats. Texture processing is designed // for non-sRGB formats. (sRGB formats require a different order of operations! // See section "Alpha Blending" in DigitalRune KB.) try { var mipmapChain = input.Faces[0]; // Mipmap chain. var level0 = mipmapChain[0]; // Most detailed mipmap level. int width = level0.Width; int height = level0.Height; // Early out? if (!ColorKeyEnabled && (!GenerateMipmaps || (width == 1 && height == 1) || // Does not need mipmaps mipmapChain.Count > 1) && // or already has mipmaps. !PremultiplyAlpha && (!ResizeToPowerOfTwo || (MathHelper.IsPowerOf2(width) && MathHelper.IsPowerOf2(height))) && !ScaleAlphaToCoverage) { if (Format == DRTextureFormat.NoChange) { // No processing required. return(input); } SurfaceFormat surfaceFormat; if (!level0.TryGetFormat(out surfaceFormat)) { throw new InvalidContentException("Surface format is not supported.", input.Identity); } if ((Format == DRTextureFormat.Color && surfaceFormat == SurfaceFormat.Color) || (Format == DRTextureFormat.Dxt && IsDxt(surfaceFormat))) { // No processing required. return(input); } } var texture = TextureHelper.ToTexture(input); var sourceFormat = texture.Description.Format; // Apply color keying. if (ColorKeyEnabled) { // Apply color keying in RGBA 8:8:8:8. texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); TextureHelper.ApplyColorKey(texture, ColorKeyColor.R, ColorKeyColor.G, ColorKeyColor.B, ColorKeyColor.A); } // Normal maps require special treatment (no sRGB, etc.). bool isNormalMap = (Format == DRTextureFormat.Normal || Format == DRTextureFormat.NormalInvertY); if (isNormalMap) { InputGamma = 1.0f; OutputGamma = 1.0f; PremultiplyAlpha = false; } // Check whether alpha channel is used. bool hasAlpha = false; // true if alpha channel != 1. bool hasFractionalAlpha = false; // true if alpha channel has 0 < alpha < 1. if (!isNormalMap) { if (GenerateMipmaps || ResizeToPowerOfTwo || PremultiplyAlpha || Format == DRTextureFormat.Dxt) { try { TextureHelper.HasAlpha(texture, out hasAlpha, out hasFractionalAlpha); } catch (NotSupportedException) { // HasAlpha() does not support the current format. Convert and try again. texture = texture.ConvertTo(DataFormat.R32G32B32A32_FLOAT); TextureHelper.HasAlpha(texture, out hasAlpha, out hasFractionalAlpha); } } } // Convert to high-precision, floating-point format for processing. texture = texture.ConvertTo(DataFormat.R32G32B32A32_FLOAT); if (!isNormalMap) { // Convert texture from gamma space to linear space. TextureHelper.GammaToLinear(texture, InputGamma); } else { // Convert normal map from [0, 1] to [-1, 1]. TextureHelper.UnpackNormals(texture); } // The resize filter needs to consider alpha if the image is not already // premultiplied. PremultiplyAlpha indicates that the source image has // alpha, but is not yet premultiplied. (Premultiplication happen at the // end.) bool alphaTransparency = hasAlpha && PremultiplyAlpha; if (ResizeToPowerOfTwo || context.TargetProfile == GraphicsProfile.Reach && Format == DRTextureFormat.Dxt) { // Resize to power-of-two. int expectedWidth = RoundUpToPowerOfTwo(texture.Description.Width); int expectedHeight = RoundUpToPowerOfTwo(texture.Description.Height); if (expectedWidth != texture.Description.Width || expectedHeight != texture.Description.Height) { texture = texture.Resize(expectedWidth, expectedHeight, texture.Description.Depth, ResizeFilter.Kaiser, alphaTransparency, TextureAddressMode.Clamp); } } if (Format == DRTextureFormat.Dxt || Format == DRTextureFormat.Normal || Format == DRTextureFormat.NormalInvertY) { // Resize to multiple of four. int expectedWidth = RoundToMultipleOfFour(texture.Description.Width); int expectedHeight = RoundToMultipleOfFour(texture.Description.Height); if (expectedWidth != texture.Description.Width || expectedHeight != texture.Description.Height) { texture = texture.Resize(expectedWidth, expectedHeight, texture.Description.Depth, ResizeFilter.Kaiser, alphaTransparency, TextureAddressMode.Clamp); } } if (GenerateMipmaps && texture.Description.MipLevels <= 1) { // Generate mipmaps. texture.GenerateMipmaps(ResizeFilter.Box, alphaTransparency, TextureAddressMode.Repeat); } // For debugging: // ColorizeMipmaps(texture); if (!isNormalMap) { if (ScaleAlphaToCoverage) { TextureHelper.ScaleAlphaToCoverage(texture, ReferenceAlpha, /* data not yet premultiplied */ false); } // Convert texture from linear space to gamma space. TextureHelper.LinearToGamma(texture, OutputGamma); // Premultiply alpha. if (hasAlpha && PremultiplyAlpha) { TextureHelper.PremultiplyAlpha(texture); } } else { // Renormalize normal map and convert to DXT5nm. TextureHelper.ProcessNormals(texture, Format == DRTextureFormat.NormalInvertY); } #if !MONOGAME // No PVRTC in XNA build. string mgPlatform = ContentHelper.GetMonoGamePlatform(); if (!string.IsNullOrEmpty(mgPlatform) && mgPlatform.ToUpperInvariant() == "IOS") { Format = DRTextureFormat.Color; } #endif // Convert to from floating-point format to requested output format. switch (Format) { case DRTextureFormat.NoChange: texture = texture.ConvertTo(sourceFormat); input = TextureHelper.ToContent(texture, input.Identity); break; case DRTextureFormat.Color: texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); input = TextureHelper.ToContent(texture, input.Identity); break; case DRTextureFormat.Dxt: if (texture.Description.Dimension == TextureDimension.Texture3D) { texture = texture.ConvertTo(DataFormat.R8G8B8A8_UNORM); input = TextureHelper.ToContent(texture, input.Identity); } else { #if MONOGAME input = Compress(context, texture, hasAlpha, hasFractionalAlpha, PremultiplyAlpha, input.Identity); #else if (hasFractionalAlpha) { texture = texture.ConvertTo(DataFormat.BC3_UNORM); } else { texture = texture.ConvertTo(DataFormat.BC1_UNORM); } input = TextureHelper.ToContent(texture, input.Identity); #endif } break; case DRTextureFormat.Normal: case DRTextureFormat.NormalInvertY: #if MONOGAME input = Compress(context, texture, true, true, false, input.Identity); #else texture = texture.ConvertTo(DataFormat.BC3_UNORM); input = TextureHelper.ToContent(texture, input.Identity); #endif break; default: throw new NotSupportedException("The specified output format is not supported."); } } catch (Exception ex) { throw new InvalidContentException(ex.Message, input.Identity); } return(input); }