Ejemplo n.º 1
0
        private static void MergeAnimation(string animationFilePath, AnimationContentDictionary animationDictionary,
                                       ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            // Use content pipeline to build the asset.
              NodeContent mergeModel = context.BuildAndLoadAsset<NodeContent, NodeContent>(new ExternalReference<NodeContent>(animationFilePath), 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.", animationFilePath);
            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}' from '{1}' with merged animation.",
              animationName, animationFilePath);

              animationDictionary[animationName] = mergeRoot.Animations[animationName];
            }
            else
            {
              context.Logger.LogImportantMessage("Merging animation '{0}' from '{1}'.", animationName, animationFilePath);

              animationDictionary.Add(animationName, mergeRoot.Animations[animationName]);
            }
              }
        }
Ejemplo n.º 2
0
		protected FragmentContent LoadFragmentContent(ContentProcessorContext context, string fileName, ContentIdentity relativeToContent = null)
		{
			ExternalReference<FragmentContent> externalReference = (relativeToContent != null)
				? new ExternalReference<FragmentContent>(fileName, relativeToContent)
				: new ExternalReference<FragmentContent>(fileName);
			return context.BuildAndLoadAsset<FragmentContent, FragmentContent>(externalReference, null);
		}
Ejemplo n.º 3
0
 /// <summary>
 /// Initializes a new instance of the InvalidContentException class with the specified error message and the identity of the content throwing the exception.
 /// </summary>
 /// <param name="message">A message that describes the error.</param>
 /// <param name="contentIdentity">Information about the content item that caused this error, including the file name. In some cases, a location within the file (of the problem) is specified.</param>
 public InvalidContentException(
     string message,
     ContentIdentity contentIdentity
     )
     : this(message, contentIdentity, null)
 {
 }
Ejemplo n.º 4
0
 /// <summary>
 /// Initializes a new instance of the InvalidContentException class with the specified error message and the identity of the content throwing the exception.
 /// </summary>
 /// <param name="message">A message that describes the error.</param>
 /// <param name="contentIdentity">Information about the content item that caused this error, including the file name. In some cases, a location within the file (of the problem) is specified.</param>
 public InvalidContentException(
     string message,
     ContentIdentity contentIdentity
     )
     : this(message, contentIdentity, null)
 {
 }
Ejemplo n.º 5
0
 public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, params object[] messageArgs)
 {
     Debug.LogWarning(message, messageArgs);
     Debug.LogWarning(contentIdentity.SourceFilename);
     Debug.LogWarning(contentIdentity.SourceTool);
     Debug.LogWarning(contentIdentity.FragmentIdentifier);
     Debug.LogWarning(helpLink);
 }
Ejemplo n.º 6
0
 /// <summary>
 /// Initializes a new instance of the InvalidContentException class with the specified error message, the identity of the content throwing the exception, and a reference to the inner exception that is the cause of this exception.
 /// </summary>
 /// <param name="message">A message that describes the error.</param>
 /// <param name="contentIdentity">Information about the content item that caused this error, including the file name. In some cases, a location within the file (of the problem) is specified.</param>
 /// <param name="innerException">The exception that is the cause of the current exception. If innerException is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
 public InvalidContentException(
     string message,
     ContentIdentity contentIdentity,
     Exception innerException
     )
     : base(message, innerException)
 {
     ContentIdentity = contentIdentity;
 }
Ejemplo n.º 7
0
 /// <summary>
 /// Initializes a new instance of the InvalidContentException class with the specified error message, the identity of the content throwing the exception, and a reference to the inner exception that is the cause of this exception.
 /// </summary>
 /// <param name="message">A message that describes the error.</param>
 /// <param name="contentIdentity">Information about the content item that caused this error, including the file name. In some cases, a location within the file (of the problem) is specified.</param>
 /// <param name="innerException">The exception that is the cause of the current exception. If innerException is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
 public InvalidContentException(
     string message,
     ContentIdentity contentIdentity,
     Exception innerException
     )
     : base(message, innerException)
 {
     ContentIdentity = contentIdentity;
 }
Ejemplo n.º 8
0
        private static List <MaterialContent> ImportMaterials(ContentIdentity identity, Scene scene)
        {
            var materials = new List <MaterialContent>();

            foreach (var sceneMaterial in scene.Materials)
            {
                var material = new BasicMaterialContent
                {
                    Name     = sceneMaterial.Name,
                    Identity = identity,
                };

                if (sceneMaterial.HasTextureDiffuse)
                {
                    var texture = new ExternalReference <TextureContent>(sceneMaterial.TextureDiffuse.FilePath, identity);
                    texture.OpaqueData.Add("TextureCoordinate", string.Format("TextureCoordinate{0}", sceneMaterial.TextureDiffuse.UVIndex));
                    material.Textures.Add("Texture", texture);
                }

                if (sceneMaterial.HasTextureOpacity)
                {
                    var texture = new ExternalReference <TextureContent>(sceneMaterial.TextureOpacity.FilePath, identity);
                    texture.OpaqueData.Add("TextureCoordinate", string.Format("TextureCoordinate{0}", sceneMaterial.TextureOpacity.UVIndex));
                    material.Textures.Add("Transparency", texture);
                }

                if (sceneMaterial.HasColorDiffuse)
                {
                    material.DiffuseColor = ToXna(sceneMaterial.ColorDiffuse);
                }

                if (sceneMaterial.HasColorEmissive)
                {
                    material.EmissiveColor = ToXna(sceneMaterial.ColorEmissive);
                }

                if (sceneMaterial.HasOpacity)
                {
                    material.Alpha = sceneMaterial.Opacity;
                }

                if (sceneMaterial.HasColorSpecular)
                {
                    material.SpecularColor = ToXna(sceneMaterial.ColorSpecular);
                }

                if (sceneMaterial.HasShininessStrength)
                {
                    material.SpecularPower = sceneMaterial.ShininessStrength;
                }

                materials.Add(material);
            }

            return(materials);
        }
Ejemplo n.º 9
0
 /// <summary>
 /// Gets the filename currently being processed, for use in warning and error messages.
 /// </summary>
 /// <param name="contentIdentity">Identity of a content item. If specified, GetCurrentFilename uses this value to refine the search. If no value is specified, the current PushFile state is used.</param>
 /// <returns>Name of the file being processed.</returns>
 protected string GetCurrentFilename(
     ContentIdentity contentIdentity
     )
 {
     if ((contentIdentity != null) && !string.IsNullOrEmpty(contentIdentity.SourceFilename))
         return GetRelativePath(contentIdentity.SourceFilename, LoggerRootDirectory);
     if (filenames.Count > 0)
         return GetRelativePath(filenames.Peek(), LoggerRootDirectory);
     return null;
 }
Ejemplo n.º 10
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            var identity = new ContentIdentity(filename, GetType().Name);

            using (var importer = new AssimpContext())
            {
                _scene = importer.ImportFile(filename,
                                             //PostProcessSteps.FindInstances | // No effect + slow?
                                             PostProcessSteps.FindInvalidData |
                                             PostProcessSteps.FlipUVs |
                                             PostProcessSteps.FlipWindingOrder |
                                             //PostProcessSteps.MakeLeftHanded | // Appears to just mess things up
                                             PostProcessSteps.JoinIdenticalVertices |
                                             PostProcessSteps.ImproveCacheLocality |
                                             PostProcessSteps.OptimizeMeshes |
                                             //PostProcessSteps.OptimizeGraph | // Will eliminate helper nodes
                                             PostProcessSteps.RemoveRedundantMaterials |
                                             PostProcessSteps.Triangulate
                                             );

                _globalInverseXform = _scene.RootNode.Transform;
                _globalInverseXform.Inverse();

                _rootNode = new NodeContent
                {
                    Name      = _scene.RootNode.Name,
                    Identity  = identity,
                    Transform = ToXna(_scene.RootNode.Transform)
                };

                _materials = ImportMaterials(identity, _scene);

                FindMeshes(_scene.RootNode, _scene.RootNode.Transform);

                if (_scene.HasAnimations)
                {
                    var skeleton = CreateSkeleton();
                    CreateAnimation(skeleton);
                }

                // If we have a simple hierarchy with no bones and just the one
                // mesh, we can flatten it out so the mesh is the root node.
                if (_rootNode.Children.Count == 1 && _rootNode.Children[0] is MeshContent)
                {
                    var absXform = _rootNode.Children[0].AbsoluteTransform;
                    _rootNode           = _rootNode.Children[0];
                    _rootNode.Identity  = identity;
                    _rootNode.Transform = absXform;
                }

                _scene.Clear();
            }

            return(_rootNode);
        }
Ejemplo n.º 11
0
 internal static Exception CreateInvalidContentException(this IntermediateReader reader, string message, params object[] messageArgs)
 {
     ContentIdentity contentIdentity = new ContentIdentity();
     contentIdentity.SourceFilename = reader.Xml.BaseURI;
     IXmlLineInfo xml = reader.Xml as IXmlLineInfo;
     if (xml != null)
     {
         contentIdentity.FragmentIdentifier = string.Format(CultureInfo.InvariantCulture, "{0},{1}", new object[] { xml.LineNumber, xml.LinePosition });
     }
     return new InvalidContentException(string.Format(CultureInfo.CurrentCulture, message, messageArgs), contentIdentity);
 }
        public override void LogWarning(string helpLink, Microsoft.Xna.Framework.Content.Pipeline.ContentIdentity contentIdentity, string message, params object[] messageArgs)
        {
            var msg      = string.Format(message, messageArgs);
            var fileName = GetCurrentFilename(contentIdentity);

            monitor.Log.WriteLine(string.Format("{0}: {1}", fileName, msg));
            if (result != null)
            {
                result.AddWarning(string.Format("{0}: {1}", fileName, msg));
            }
        }
Ejemplo n.º 13
0
 protected string GetCurrentFilename(ContentIdentity contentIdentity)
 {
     if ((contentIdentity != null) && !string.IsNullOrEmpty(contentIdentity.SourceFilename))
     {
         return(GetFilename(contentIdentity.SourceFilename, loggerRootDirectory));
     }
     if (this.fileStack.Count > 0)
     {
         return(GetFilename(fileStack.Peek(), loggerRootDirectory));
     }
     return(null);
 }
Ejemplo n.º 14
0
 public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, params object[] messageArgs)
 {
     var warning = string.Empty;
     if (contentIdentity != null && !string.IsNullOrEmpty(contentIdentity.SourceFilename))
     {
         warning = contentIdentity.SourceFilename;
         if (!string.IsNullOrEmpty(contentIdentity.FragmentIdentifier))
             warning += "(" + contentIdentity.FragmentIdentifier + ")";
         warning += ": ";
     }
     warning += string.Format(message, messageArgs);
     Console.WriteLine(warning);
 }
Ejemplo n.º 15
0
        /// <summary>
        /// The main Process method converts an intermediate format content pipeline
        /// NodeContent tree to a ModelContent object with embedded animation data.
        /// </summary>
        public override ModelContent Process(NodeContent input,
            ContentProcessorContext context)
        {
            rootIdentity = input.Identity;

            ValidateMesh(input, context, null);

            // Find the skeleton.
            BoneContent skeleton = MeshHelper.FindSkeleton(input);

            if (skeleton == null)
                throw new InvalidContentException("Input skeleton not found.");

            // We don't want to have to worry about different parts of the model being
            // in different local coordinate systems, so let's just bake everything.
            FlattenTransforms(input, skeleton);

            // Read the bind pose and skeleton hierarchy data.
            IList<BoneContent> bones = MeshHelper.FlattenSkeleton(skeleton);

            if (bones.Count > SkinnedEffect.MaxBones)
            {
                throw new InvalidContentException(string.Format(
                    "Skeleton has {0} bones, but the maximum supported is {1}.",
                    bones.Count, SkinnedEffect.MaxBones));
            }

            List<Matrix> bindPose = new List<Matrix>();
            List<Matrix> inverseBindPose = new List<Matrix>();
            List<int> skeletonHierarchy = new List<int>();

            foreach (BoneContent bone in bones)
            {
                bindPose.Add(bone.Transform);
                inverseBindPose.Add(Matrix.Invert(bone.AbsoluteTransform));
                skeletonHierarchy.Add(bones.IndexOf(bone.Parent as BoneContent));
            }

            // Convert animation data to our runtime format.
            Dictionary<string, AnimationClip> animationClips;
            animationClips = ProcessAnimations(skeleton.Animations, bones);

            // Chain to the base ModelProcessor class so it can convert the model data.
            ModelContent model = base.Process(input, context);

            // Store our custom animation data in the Tag property of the model.
            model.Tag = new SkinningData(animationClips, bindPose,
                                         inverseBindPose, skeletonHierarchy);

            return model;
        }
        /// <summary>
        /// Converts a DigitalRune <see cref="Texture"/> to an XNA <see cref="TextureContent"/>.
        /// </summary>
        /// <param name="texture">The <see cref="Texture"/>.</param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The <see cref="TextureContent"/>.</returns>
        public static TextureContent ToContent(Texture texture, ContentIdentity identity)
        {
            var description = texture.Description;
              switch (description.Dimension)
              {
            case TextureDimension.Texture1D:
            case TextureDimension.Texture2D:
              {
            var textureContent = new Texture2DContent { Identity = identity };
            for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++)
            {
              var image = texture.Images[texture.GetImageIndex(mipIndex, 0, 0)];
              textureContent.Mipmaps.Add(ToContent(image));
            }

            return textureContent;
              }
            case TextureDimension.TextureCube:
              {
            var textureContent = new TextureCubeContent { Identity = identity };
            for (int faceIndex = 0; faceIndex < 6; faceIndex++)
            {
              for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++)
              {
                var image = texture.Images[texture.GetImageIndex(mipIndex, faceIndex, 0)];
                textureContent.Faces[faceIndex].Add(ToContent(image));
              }
            }

            return textureContent;
              }
            case TextureDimension.Texture3D:
              {
            var textureContent = new Texture3DContent { Identity = identity };
            for (int zIndex = 0; zIndex < description.Depth; zIndex++)
            {
              textureContent.Faces.Add(new MipmapChain());
              for (int mipIndex = 0; mipIndex < description.MipLevels; mipIndex++)
              {
                var image = texture.Images[texture.GetImageIndex(mipIndex, 0, zIndex)];
                textureContent.Faces[zIndex].Add(ToContent(image));
              }
            }

            return textureContent;
              }
              }

              throw new InvalidOperationException("Invalid texture dimension.");
        }
Ejemplo n.º 17
0
        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);
        }
 /// <summary>
 /// Gets the filename currently being processed, for use in warning and error messages.
 /// </summary>
 /// <param name="contentIdentity">Identity of a content item. If specified, GetCurrentFilename uses this value to refine the search. If no value is specified, the current PushFile state is used.</param>
 /// <returns>Name of the file being processed.</returns>
 protected string GetCurrentFilename(
     ContentIdentity contentIdentity
     )
 {
     if ((contentIdentity != null) && !string.IsNullOrEmpty(contentIdentity.SourceFilename))
     {
         return(GetRelativePath(contentIdentity.SourceFilename, LoggerRootDirectory));
     }
     if (filenames.Count > 0)
     {
         return(GetRelativePath(filenames.Peek(), LoggerRootDirectory));
     }
     return(null);
 }
Ejemplo n.º 19
0
        /// <summary>
        /// Override the Process method to store the ContentIdentity of the model root node.
        /// </summary>
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            rootIdentity = input.Identity;
            string modelName = input.Identity.SourceFilename.Substring(input.Identity.SourceFilename.LastIndexOf("\\") + 1);
            this.context = context;
            Dictionary<string, object> ModelData = new Dictionary<string, object>();

            ModelContent baseModel = base.Process(input, context);
            GenerateData(input);
            ModelData.Add("BBox", boxs);
            ModelData.Add("BSphere", spheres);
            baseModel.Tag = ModelData;

            return baseModel;
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Merges the specified animation files to the specified animation dictionary.
        /// </summary>
        /// <param name="animationFiles">
        /// The animation files as a string separated by semicolon (relative to the folder of the model 
        /// file). For example: "run.fbx;jump.fbx;turn.fbx".
        /// </param>
        /// <param name="animationDictionary">The animation dictionary.</param>
        /// <param name="contentIdentity">The content identity.</param>
        /// <param name="context">The content processor context.</param>
        public static void Merge(string animationFiles, AnimationContentDictionary animationDictionary,
            ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            if (string.IsNullOrEmpty(animationFiles))
            return;

              // Get path of the model file.
              var files = animationFiles.Split(';', ',')
                                .Select(s => s.Trim())
                                .Where(s => !string.IsNullOrEmpty(s));
              foreach (string file in files)
              {
            MergeAnimation(file, animationDictionary, contentIdentity, context);
              }
        }
Ejemplo n.º 21
0
 /// <summary>
 /// Initializes a new instance of ExternalReference, specifying the file path relative to another content item.
 /// </summary>
 /// <param name="filename">The name of the referenced file.</param>
 /// <param name="relativeToContent">The content that the path specified in filename is relative to.</param>
 public ExternalReference(string filename, ContentIdentity relativeToContent)
 {
     if (string.IsNullOrEmpty(filename))
     {
         throw new ArgumentNullException("filename");
     }
     if (relativeToContent == null)
     {
         throw new ArgumentNullException("relativeToContent");
     }
     if (string.IsNullOrEmpty(relativeToContent.SourceFilename))
     {
         throw new ArgumentNullException("relativeToContent.SourceFilename");
     }
     Filename = Path.Combine(relativeToContent.SourceFilename, filename);
 }
Ejemplo n.º 22
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            var identity = new ContentIdentity(filename, GetType().Name);

            using (var importer = new AssimpContext())
            {
                _scene = importer.ImportFile(filename,
                                             //PostProcessSteps.FindInstances | // No effect + slow?
                                             PostProcessSteps.FindInvalidData |
                                             PostProcessSteps.FlipUVs |
                                             PostProcessSteps.FlipWindingOrder |
                                             //PostProcessSteps.MakeLeftHanded | // Appears to just mess things up
                                             PostProcessSteps.JoinIdenticalVertices |
                                             PostProcessSteps.ImproveCacheLocality |
                                             PostProcessSteps.OptimizeMeshes |
                                             //PostProcessSteps.OptimizeGraph | // Will eliminate helper nodes
                                             PostProcessSteps.RemoveRedundantMaterials |
                                             PostProcessSteps.Triangulate
                                             );

                _globalInverseXform = _scene.RootNode.Transform;
                _globalInverseXform.Inverse();

                _rootNode = new NodeContent
                {
                    Name      = _scene.RootNode.Name,
                    Identity  = identity,
                    Transform = ToXna(_scene.RootNode.Transform)
                };

                _materials = ImportMaterials(identity, _scene);

                FindMeshes(_scene.RootNode, _scene.RootNode.Transform);

                if (_scene.HasAnimations)
                {
                    var skeleton = CreateSkeleton();
                    CreateAnimation(skeleton);
                }

                _scene.Clear();
            }

            return(_rootNode);
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Merges the specified animation files to the specified animation dictionary.
        /// </summary>
        /// <param name="animationFiles">
        /// The animation files as a string separated by semicolon (relative to the folder of the model 
        /// file). For example: "run.fbx;jump.fbx;turn.fbx".
        /// </param>
        /// <param name="animationDictionary">The animation dictionary.</param>
        /// <param name="contentIdentity">The content identity.</param>
        /// <param name="context">The content processor context.</param>
        public static void Merge(string animationFiles, AnimationContentDictionary animationDictionary,
                             ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            if (string.IsNullOrEmpty(animationFiles))
            return;

            // Get path of the model file.
            string sourcePath = Path.GetDirectoryName(contentIdentity.SourceFilename);

              var files = animationFiles.Split(';')
                                .Select(s => s.Trim())
                                .Where(s => !string.IsNullOrEmpty(s));
              foreach (string file in files)
            {
              string filePath = Path.GetFullPath(Path.Combine(sourcePath, file));
              MergeAnimation(filePath, animationDictionary, contentIdentity, context);
            }
        }
Ejemplo n.º 24
0
 public ExternalReference(string filename, ContentIdentity relativeToContent)
 {
     if (filename != null)
     {
         if (filename.Length == 0)
         {
             throw new ArgumentNullException("filename");
         }
         if (relativeToContent == null)
         {
             throw new ArgumentNullException("relativeToContent");
         }
         if (string.IsNullOrEmpty(relativeToContent.SourceFilename))
         {
             throw new ArgumentNullException("relativeToContent.SourceFilename");
         }
         this.filename = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(relativeToContent.SourceFilename), filename));
     }
 }
Ejemplo n.º 25
0
        /// <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);
        }
Ejemplo n.º 26
0
        /// <summary>
        /// Initializes a new instance of ExternalReference, specifying the file path relative to another content item.
        /// </summary>
        /// <param name="filename">The name of the referenced file.</param>
        /// <param name="relativeToContent">The content that the path specified in filename is relative to.</param>
        public ExternalReference(string filename, ContentIdentity relativeToContent)
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new ArgumentNullException("filename");
            }
            if (relativeToContent == null)
            {
                throw new ArgumentNullException("relativeToContent");
            }
            if (string.IsNullOrEmpty(relativeToContent.SourceFilename))
            {
                throw new ArgumentNullException("relativeToContent.SourceFilename");
            }

            // The intermediate serializer from XNA has the external reference
            // path walking up to the content project directory and then back
            // down to the asset path. We don't appear to have any way to do
            // that from here, so we'll work with the absolute path and let the
            // higher level process sort out any relative paths they need.
            var basePath = Path.GetDirectoryName(relativeToContent.SourceFilename);

            Filename = PathHelper.NormalizeOS(Path.GetFullPath(Path.Combine(basePath, filename)));
        }
Ejemplo n.º 27
0
        public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, params object[] messageArgs)
        {
            var warning = new StringBuilder();

            if (!string.IsNullOrEmpty(contentIdentity?.SourceFilename))
            {
                warning.Append(contentIdentity.SourceFilename);
                if (!string.IsNullOrEmpty(contentIdentity.FragmentIdentifier))
                {
                    warning.Append(" (");
                    warning.Append(contentIdentity.FragmentIdentifier);
                    warning.Append(")");
                }

                warning.Append(": ");
            }

            if (messageArgs != null && messageArgs.Length > 0)
                warning.AppendFormat(CultureInfo.InvariantCulture, message, messageArgs);
            else
                warning.Append(message);

            _outputService.WriteLine(warning.ToString());
        }
Ejemplo n.º 28
0
 public InvalidContentException(string message, ContentIdentity contentIdentity)
     : base(message)
 {
     ContentIdentity = contentIdentity;
 }
Ejemplo n.º 29
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            var identity = new ContentIdentity(filename, GetType().Name);
            var importer = new AssimpImporter();

            importer.AttachLogStream(new LogStream((msg, userData) => context.Logger.LogMessage(msg)));
            var scene = importer.ImportFile(filename,
                                            PostProcessSteps.FlipUVs | // So far appears necessary
                                            PostProcessSteps.JoinIdenticalVertices |
                                            PostProcessSteps.Triangulate |
                                            PostProcessSteps.SortByPrimitiveType |
                                            PostProcessSteps.FindInvalidData
                                            );

            var rootNode = new NodeContent
            {
                Name      = scene.RootNode.Name,
                Identity  = identity,
                Transform = ToXna(scene.RootNode.Transform)
            };

            // TODO: Materials
            var materials = new List <MaterialContent>();

            foreach (var sceneMaterial in scene.Materials)
            {
                var diffuse = sceneMaterial.GetTexture(TextureType.Diffuse, 0);

                materials.Add(new BasicMaterialContent()
                {
                    Name     = sceneMaterial.Name,
                    Identity = identity,
                    Texture  = new ExternalReference <TextureContent>(diffuse.FilePath, identity)
                });
            }

            // Meshes
            var meshes = new Dictionary <Mesh, MeshContent>();

            foreach (var sceneMesh in scene.Meshes)
            {
                if (!sceneMesh.HasVertices)
                {
                    continue;
                }

                var mesh = new MeshContent
                {
                    Name = sceneMesh.Name
                };

                // Position vertices are shared at the mesh level
                foreach (var vert in sceneMesh.Vertices)
                {
                    mesh.Positions.Add(new Vector3(vert.X, vert.Y, vert.Z));
                }

                var geom = new GeometryContent
                {
                    Name = string.Empty,
                    //Material = materials[sceneMesh.MaterialIndex]
                };

                // Geometry vertices reference 1:1 with the MeshContent parent,
                // no indirection is necessary.
                geom.Vertices.Positions.AddRange(mesh.Positions);
                geom.Vertices.AddRange(Enumerable.Range(0, sceneMesh.VertexCount));
                geom.Indices.AddRange(sceneMesh.GetIntIndices());

                // Individual channels go here
                if (sceneMesh.HasNormals)
                {
                    geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(sceneMesh.Normals));
                }

                for (var i = 0; i < sceneMesh.TextureCoordsChannelCount; i++)
                {
                    geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i),
                                               ToXnaVector2(sceneMesh.GetTextureCoords(i)));
                }

                mesh.Geometry.Add(geom);
                rootNode.Children.Add(mesh);
                meshes.Add(sceneMesh, mesh);
            }

            // Bones
            var bones          = new Dictionary <Node, BoneContent>();
            var hierarchyNodes = scene.RootNode.Children.SelectDeep(n => n.Children).ToList();

            foreach (var node in hierarchyNodes)
            {
                var bone = new BoneContent
                {
                    Name      = node.Name,
                    Transform = Matrix.Transpose(ToXna(node.Transform))
                };

                if (node.Parent == scene.RootNode)
                {
                    rootNode.Children.Add(bone);
                }
                else
                {
                    var parent = bones[node.Parent];
                    parent.Children.Add(bone);
                }

                // Copy the bone's name to the MeshContent - this appears to be
                // the way it comes out of XNA's FBXImporter.
                foreach (var meshIndex in node.MeshIndices)
                {
                    meshes[scene.Meshes[meshIndex]].Name = node.Name;
                }

                bones.Add(node, bone);
            }

            return(rootNode);
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Entry point for animation processing. 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="input"></param>
        /// <param name="context"></param>
        private void ProcessAnimations(ModelContent model, NodeContent input, ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // First build a lookup table so we can determine the
            // index into the list of bones from a bone name.
            for (int i = 0; i < model.Bones.Count; i++)
            {
                bones[model.Bones[i].Name] = i;
            }

            // For saving the bone transforms
            boneTransforms = new Matrix[model.Bones.Count];

            //
            // Collect up all of the animation data
            //

            ProcessAnimationsRecursive(input);

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                // Add the filename as a dependency, so if it changes, the model is rebuilt
                context.AddDependency(AnimPath);

                // Load the animation definition from the XML file
                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                if (modelExtra.Clips.Count > 0) //if there are some animations in our model
                {
                    foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts)
                    {
                        // Grab the main clip that we are using and copy to MainClip

                        AnimationClip MainClip = new AnimationClip();

                        float StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration);
                        float EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration);

                        MainClip.Duration = EndTime-StartTime;
                        MainClip.Name = modelExtra.Clips[AnimDef.OriginalClipName].Name;

                        // Process each of our new animation clip parts
                        for (int i = 0; i < modelExtra.Clips[AnimDef.OriginalClipName].Bones.Count; i++)
                        {
                            AnimationClip.Bone clipBone = new AnimationClip.Bone();
                            clipBone.Name = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Name;
                            LinkedList<AnimationClip.Keyframe> keyframes = new LinkedList<AnimationClip.Keyframe>();

                            if (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count != 0)
                            {

                                for (int j = 0; j < modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count; j++)
                                {

                                    if ((modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time >= StartTime) && (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time <= EndTime))
                                    {
                                        AnimationClip.Keyframe frame = new AnimationClip.Keyframe();
                                        frame.Rotation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Rotation;
                                        frame.Time = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time - StartTime;
                                        frame.Translation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Translation;
                                        keyframes.AddLast(frame);
                                       //clipBone.Keyframes.Add(frame);
                                    }

                                }
                            }
                           // LinearKeyframeReduction(keyframes);
                            clipBone.Keyframes = keyframes.ToList<AnimationClip.Keyframe>();
                            MainClip.Bones.Add(clipBone);

                        }
                        modelExtra.Clips.Add(Part.ClipName, MainClip);

                    }
                }
            }

            // Ensure there is always a clip, even if none is included in the FBX
            // That way we can create poses using FBX files as one-frame
            // animation clips
            if (modelExtra.Clips.Count == 0)
            {
                AnimationClip clip = new AnimationClip();
                modelExtra.Clips.Add("Take 001",clip);

                string clipName = "Take 001";

                // Retain by name
                clips[clipName] = clip;

                clip.Name = clipName;
                foreach (ModelBoneContent bone in model.Bones)
                {
                    AnimationClip.Bone clipBone = new AnimationClip.Bone();
                    clipBone.Name = bone.Name;

                    clip.Bones.Add(clipBone);
                }
            }

            //Ensure all animations have a first key frame for every bone
            foreach (KeyValuePair<string,AnimationClip> clip in modelExtra.Clips)
            {
                for (int b = 0; b < bones.Count; b++)
                {
                    List<AnimationClip.Keyframe> keyframes = clip.Value.Bones[b].Keyframes;
                    if (keyframes.Count == 0 || keyframes[0].Time > 0)
                    {
                        AnimationClip.Keyframe keyframe = new AnimationClip.Keyframe();
                        keyframe.Time = 0;
                        keyframe.Transform = boneTransforms[b];
                        keyframes.Insert(0, keyframe);
                    }
                }
            }
        }
Ejemplo n.º 31
0
        /// <summary>
        /// Parses the animation split definitions defined by the specified XML element.
        /// </summary>
        /// <param name="animationsElement">The XML element that defines the animation splits.</param>
        /// <param name="contentIdentity">The content identity.</param>
        /// <param name="context">The context.</param>
        /// <returns>The list of animation split definitions.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="animationsElement"/>, <paramref name="contentIdentity"/>, or 
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        internal static List<AnimationSplitDefinition> ParseAnimationSplitDefinitions(XElement animationsElement, ContentIdentity contentIdentity, ContentPipelineContext context)
        {
            if (animationsElement == null)
            throw new ArgumentNullException("animationsElement");
              if (contentIdentity == null)
            throw new ArgumentNullException("contentIdentity");
              if (context == null)
            throw new ArgumentNullException("context");

              // The frame rate needs to be set, when the splits are defined in frames.
              double? framerate = (double?)animationsElement.Attribute("Framerate");

              // Read all animation splits.
              var splits = new List<AnimationSplitDefinition>();
              foreach (var animationElement in animationsElement.Elements("Animation"))
              {
            var name = animationElement.GetMandatoryAttribute("Name", contentIdentity);

            double? startTime = (double?)animationElement.Attribute("StartTime");
            double? endTime = (double?)animationElement.Attribute("EndTime");

            int? startFrame = (int?)animationElement.Attribute("StartFrame");
            int? endFrame = (int?)animationElement.Attribute("EndFrame");

            bool? addLoopFrame = (bool?)animationsElement.Attribute("AddLoopFrame");

            if (startTime == null && startFrame == null)
            {
              string message = XmlHelper.GetExceptionMessage(animationElement, "The animation element does not contain a valid \"StartTime\" or \"StartFrame\" attribute.");
              throw new InvalidContentException(message, contentIdentity);
            }

            if (endTime == null && endFrame == null)
            {
              string message = XmlHelper.GetExceptionMessage(animationElement, "The animation element does not contain a valid \"EndTime\" or \"EndFrame\" attribute.");
              throw new InvalidContentException(message, contentIdentity);
            }

            if (framerate == null && (startTime == null || endTime == null))
            {
              string message = XmlHelper.GetExceptionMessage(animationsElement, "The animations element must have a <Framerate> element if start and end are specified in frames.");
              throw new InvalidContentException(message, contentIdentity);
            }

            startTime = startTime ?? startFrame.Value / framerate.Value;
            endTime = endTime ?? endFrame.Value / framerate.Value;
            TimeSpan start = new TimeSpan((long)(startTime.Value * TimeSpan.TicksPerSecond));
            TimeSpan end = new TimeSpan((long)(endTime.Value * TimeSpan.TicksPerSecond));

            if (start > end)
            {
              string message = XmlHelper.GetExceptionMessage(animationElement, "Invalid animation element: The start time is larger than the end time.");
              throw new InvalidContentException(message, contentIdentity);
            }

            splits.Add(new AnimationSplitDefinition { Name = name, StartTime = start, EndTime = end, AddLoopFrame = addLoopFrame });
              }

              return splits;
        }
Ejemplo n.º 32
0
 public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, object[] messageArgs)
 {
     log.LogWarning(message, messageArgs);
 }
Ejemplo n.º 33
0
        static internal TextureContent Import(string filename, ContentImporterContext context)
        {
            var            identity = new ContentIdentity(filename);
            TextureContent output   = null;

            using (var reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
            {
                // Read signature ("DDS ")
                var valid = reader.ReadByte() == 0x44;
                valid = valid && reader.ReadByte() == 0x44;
                valid = valid && reader.ReadByte() == 0x53;
                valid = valid && reader.ReadByte() == 0x20;
                if (!valid)
                {
                    throw new ContentLoadException("Invalid file signature");
                }

                var header = new DdsHeader();

                // Read DDS_HEADER
                header.dwSize = reader.ReadUInt32();
                if (header.dwSize != 124)
                {
                    throw new ContentLoadException("Invalid DDS_HEADER dwSize value");
                }
                header.dwFlags             = (Ddsd)reader.ReadUInt32();
                header.dwHeight            = reader.ReadUInt32();
                header.dwWidth             = reader.ReadUInt32();
                header.dwPitchOrLinearSize = reader.ReadUInt32();
                header.dwDepth             = reader.ReadUInt32();
                header.dwMipMapCount       = reader.ReadUInt32();
                // The next 11 DWORDs are reserved and unused
                for (int i = 0; i < 11; ++i)
                {
                    reader.ReadUInt32();
                }
                // Read DDS_PIXELFORMAT
                header.ddspf.dwSize = reader.ReadUInt32();
                if (header.ddspf.dwSize != 32)
                {
                    throw new ContentLoadException("Invalid DDS_PIXELFORMAT dwSize value");
                }
                header.ddspf.dwFlags       = (Ddpf)reader.ReadUInt32();
                header.ddspf.dwFourCC      = (FourCC)reader.ReadUInt32();
                header.ddspf.dwRgbBitCount = reader.ReadUInt32();
                header.ddspf.dwRBitMask    = reader.ReadUInt32();
                header.ddspf.dwGBitMask    = reader.ReadUInt32();
                header.ddspf.dwBBitMask    = reader.ReadUInt32();
                header.ddspf.dwABitMask    = reader.ReadUInt32();
                // Continue reading DDS_HEADER
                header.dwCaps  = (DdsCaps)reader.ReadUInt32();
                header.dwCaps2 = (DdsCaps2)reader.ReadUInt32();
                // dwCaps3 unused
                reader.ReadUInt32();
                // dwCaps4 unused
                reader.ReadUInt32();
                // dwReserved2 unused
                reader.ReadUInt32();

                // Check for the existence of the DDS_HEADER_DXT10 struct next
                if (header.ddspf.dwFlags == Ddpf.FourCC && header.ddspf.dwFourCC == FourCC.Dx10)
                {
                    throw new ContentLoadException("Unsupported DDS_HEADER_DXT10 struct found");
                }

                int faceCount   = 1;
                int mipMapCount = (int)(header.dwCaps.HasFlag(DdsCaps.MipMap) ? header.dwMipMapCount : 1);
                if (header.dwCaps.HasFlag(DdsCaps.Complex))
                {
                    if (header.dwCaps2.HasFlag(DdsCaps2.Cubemap))
                    {
                        if (!header.dwCaps2.HasFlag(DdsCaps2.CubemapAllFaces))
                        {
                            throw new ContentLoadException("Incomplete cubemap in DDS file");
                        }
                        faceCount = 6;
                        output    = new TextureCubeContent()
                        {
                            Identity = identity
                        };
                    }
                    else
                    {
                        output = new Texture2DContent()
                        {
                            Identity = identity
                        };
                    }
                }
                else
                {
                    output = new Texture2DContent()
                    {
                        Identity = identity
                    };
                }

                bool rbSwap;
                var  format = GetSurfaceFormat(ref header.ddspf, out rbSwap);

                for (int f = 0; f < faceCount; ++f)
                {
                    var w       = (int)header.dwWidth;
                    var h       = (int)header.dwHeight;
                    var mipMaps = new MipmapChain();
                    for (int m = 0; m < mipMapCount; ++m)
                    {
                        var content   = CreateBitmapContent(format, w, h);
                        var byteCount = GetBitmapSize(format, w, h);
                        var bytes     = reader.ReadBytes(byteCount);
                        content.SetPixelData(bytes);
                        mipMaps.Add(content);
                        w = MathHelper.Max(1, w / 2);
                        h = MathHelper.Max(1, h / 2);
                    }
                    output.Faces[f] = mipMaps;
                }
            }

            return(output);
        }
        /*static*/
        private void OptimizeForCache(IList<Vector3F> positions,   // In: Original positions.
            IList<int> indices,          // In: Original indices. Out: Optimized indices.
            out int[] vertexRemap,       // Maps original vertex location to optimized vertex location.
            out int[] duplicateVertices, // Original locations of duplicate vertices.
            ContentIdentity identity)
        {
            Debug.Assert(positions != null);
              Debug.Assert(indices != null);

            #if COMPUTE_VERTEX_CACHE_MISS_RATE
              float acmrOld;
              float atvrOld;
              DirectXMesh.ComputeVertexCacheMissRate(indices, positions.Count, DirectXMesh.OPTFACES_V_DEFAULT, out acmrOld, out atvrOld);
            #endif

              int numberOfVertices = positions.Count;

              int[] pointRep;
              int[] adjacency;
              DirectXMesh.GenerateAdjacencyAndPointReps(indices, positions, Numeric.EpsilonF, out pointRep, out adjacency);

              if (!DirectXMesh.Clean(indices, numberOfVertices, adjacency, null, false, out duplicateVertices))
              {
            List<string> validationMessages = new List<string>();
            DirectXMesh.Validate(indices, numberOfVertices, adjacency, MeshValidationOptions.Default, validationMessages);

            string message;
            if (validationMessages.Count == 0)
            {
              message = "Mesh cleaning failed.";
            }
            else
            {
              var messageBuilder = new StringBuilder();
              messageBuilder.AppendLine("Mesh cleaning failed:");
              foreach (var validationMessage in validationMessages)
            messageBuilder.AppendLine(validationMessage);

              message = messageBuilder.ToString();
            }

            throw new InvalidContentException(message, identity);
              }

              // Skip DirectXMesh.AttributeSort and DirectXMesh.ReorderIBAndAdjacency.
              // (GeometryContent already sorted.)

              int[] faceRemap;
              DirectXMesh.OptimizeFaces(indices, adjacency, null, out faceRemap);

              DirectXMesh.ReorderIB(indices, faceRemap);

              DirectXMesh.OptimizeVertices(indices, numberOfVertices, out vertexRemap);

              DirectXMesh.FinalizeIB(indices, vertexRemap);

              // Skip DirectXMesh.FinalizeVB.
              // (Needs to be handled by caller.)

              Debug.Assert(vertexRemap.Length == numberOfVertices + duplicateVertices.Length);

            #if COMPUTE_VERTEX_CACHE_MISS_RATE
              int newNumberOfVertices = vertexRemap.Count(i => i != -1);
              float acmrNew;
              float atvrNew;
              DirectXMesh.ComputeVertexCacheMissRate(indices, newNumberOfVertices, DirectXMesh.OPTFACES_V_DEFAULT, out acmrNew, out atvrNew);

              _context.Logger.LogMessage(
            "Mesh optimization: Vertices before {0}, after {1}; ACMR before {2}, after {3}; ATVR before {4}, after {5}",
            numberOfVertices, newNumberOfVertices, acmrOld, acmrNew, atvrOld, atvrNew);
            #endif
        }
Ejemplo n.º 35
0
        /// <summary>
        /// Converts to the specified <see cref="XAttribute"/> to a <see cref="Vector3"/>.
        /// </summary>
        /// <param name="attribute">The XML attribute to parse. Can be <see langword="null"/>.</param>
        /// <param name="defaultValue">
        /// The default value, used if <paramref name="attribute"/> is null or empty.
        /// </param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The 3D vector.</returns>
        /// <exception cref="InvalidContentException">
        /// Error parsing <paramref name="attribute"/>.
        /// </exception>
        public static Vector3 ToVector3(this XAttribute attribute, Vector3 defaultValue, ContentIdentity identity)
        {
            string value = (string)attribute;
              if (value == null)
            return defaultValue;

              try
              {
            var values = value.Split(ListSeparators, StringSplitOptions.RemoveEmptyEntries);
            if (values.Length == 3)
            {
              Vector3 result;
              result.X = float.Parse(values[0], CultureInfo.InvariantCulture);
              result.Y = float.Parse(values[1], CultureInfo.InvariantCulture);
              result.Z = float.Parse(values[2], CultureInfo.InvariantCulture);
              return result;
            }
            else
            {
              var message = GetExceptionMessage(attribute, "Could not parse 3-dimensional vector: '{0}'", value);
              throw new InvalidContentException(message, identity);
            }
              }
              catch (Exception exception)
              {
            var message = GetExceptionMessage(attribute, "Could not parse 3-dimensional vector: '{0}'", value);
            throw new InvalidContentException(message, identity, exception);
              }
        }
Ejemplo n.º 36
0
        /// <summary>
        /// Converts the specified <see cref="XAttribute"/> to a <see cref="DRTextureFormat"/> value.
        /// </summary>
        /// <param name="attribute">The XML attribute to parse. Can be <see langword="null"/>.</param>
        /// <param name="defaultValue">
        /// The default value, used if <paramref name="attribute"/> is null or empty.
        /// </param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The texture format.</returns>
        /// <exception cref="InvalidContentException">
        /// Error parsing <paramref name="attribute"/>.
        /// </exception>
        public static DRTextureFormat ToTextureFormat(this XAttribute attribute, DRTextureFormat defaultValue, ContentIdentity identity)
        {
            string value = (string)attribute;
              if (string.IsNullOrWhiteSpace(value))
            return defaultValue;

              switch (value.Trim().ToUpperInvariant())
              {
            case "NOCHANGE":
              return DRTextureFormat.NoChange;
            case "COLOR":
              return DRTextureFormat.Color;
            case "DXT":
              return DRTextureFormat.Dxt;
            case "NORMAL":
              return DRTextureFormat.Normal;
            case "NORMALINVERTY":
              return DRTextureFormat.NormalInvertY;
            default:
              var message = GetExceptionMessage(attribute, "Could not parse texture format: '{0}'", value);
              throw new InvalidContentException(message, identity);
              }
        }
Ejemplo n.º 37
0
        /// <summary>
        /// Converts the specified <see cref="XAttribute"/> to an effect parameter value.
        /// </summary>
        /// <param name="attribute">The XML attribute to parse. Can be <see langword="null"/>.</param>
        /// <param name="defaultValue">
        /// The default value, used if <paramref name="attribute"/> is null or empty.
        /// </param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The effect parameter value.</returns>
        /// <exception cref="InvalidContentException">
        /// Error parsing <paramref name="attribute"/>.
        /// </exception>
        public static object ToParameterValue(this XAttribute attribute, object defaultValue, ContentIdentity identity)
        {
            // TODO: Add support for Int32, Quaternion, Matrix.
              string value = (string)attribute;
              if (value == null)
            return defaultValue;

              try
              {
            var values = value.Split(ListSeparators, StringSplitOptions.RemoveEmptyEntries);

            // Empty value
            if (values.Length == 0)
              return defaultValue;

            if (values.Any(IsBoolean))
            {
              // Boolean
              if (values.Length == 1)
            return bool.Parse(values[0]);

              // Boolean[]
              bool[] array = new bool[values.Length];
              for (int i = 0; i < values.Length; i++)
            array[i] = bool.Parse(values[i]);

              return array;
            }
            else
            {
              // Single
              if (values.Length == 1)
            return float.Parse(values[0], CultureInfo.InvariantCulture);

              // Vector2
              if (values.Length == 2)
              {
            Vector2 result;
            result.X = float.Parse(values[0], CultureInfo.InvariantCulture);
            result.Y = float.Parse(values[1], CultureInfo.InvariantCulture);
            return result;
              }

              // Vector3
              if (values.Length == 3)
              {
            Vector3 result;
            result.X = float.Parse(values[0], CultureInfo.InvariantCulture);
            result.Y = float.Parse(values[1], CultureInfo.InvariantCulture);
            result.Z = float.Parse(values[2], CultureInfo.InvariantCulture);
            return result;
              }

              // Vector4
              if (values.Length == 4)
              {
            Vector4 result;
            result.X = float.Parse(values[0], CultureInfo.InvariantCulture);
            result.Y = float.Parse(values[1], CultureInfo.InvariantCulture);
            result.Z = float.Parse(values[2], CultureInfo.InvariantCulture);
            result.W = float.Parse(values[3], CultureInfo.InvariantCulture);
            return result;
              }
              else
              {
            // Single[]
            float[] array = new float[values.Length];
            for (int i = 0; i < values.Length; i++)
              array[i] = float.Parse(values[i], CultureInfo.InvariantCulture);

            return array;
              }
            }
              }
              catch (Exception exception)
              {
            var message = GetExceptionMessage(attribute, "Could not parse parameter value: '{0}'", value);
            throw new InvalidContentException(message, identity, exception);
              }
        }
Ejemplo n.º 38
0
        /// <summary>
        /// Converts the specified <see cref="XAttribute"/> to a color value.
        /// </summary>
        /// <param name="attribute">The XML attribute to parse. Can be <see langword="null"/>.</param>
        /// <param name="defaultValue">
        /// The default value, used if <paramref name="attribute"/> is null or empty.
        /// </param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The color value.</returns>
        /// <exception cref="InvalidContentException">
        /// Error parsing <paramref name="attribute"/>.
        /// </exception>
        public static Color ToColor(this XAttribute attribute, Color defaultValue, ContentIdentity identity)
        {
            string value = (string)attribute;
              if (value == null)
            return defaultValue;

              try
              {
            var values = value.Split(ListSeparators, StringSplitOptions.RemoveEmptyEntries);
            if (values.Length == 3)
            {
              Color color = new Color();
              color.R = byte.Parse(values[0], CultureInfo.InvariantCulture);
              color.G = byte.Parse(values[1], CultureInfo.InvariantCulture);
              color.B = byte.Parse(values[2], CultureInfo.InvariantCulture);
              color.A = byte.MaxValue;
              return color;
            }
            else if (values.Length == 4)
            {
              Color color = new Color();
              color.R = byte.Parse(values[0], CultureInfo.InvariantCulture);
              color.G = byte.Parse(values[1], CultureInfo.InvariantCulture);
              color.B = byte.Parse(values[2], CultureInfo.InvariantCulture);
              color.A = byte.Parse(values[3], CultureInfo.InvariantCulture);
              return color;
            }
            else
            {
              var message = GetExceptionMessage(attribute, "Could not parse color value: '{0}'", value);
              throw new InvalidContentException(message, identity);
            }
              }
              catch (Exception exception)
              {
            var message = GetExceptionMessage(attribute, "Could not parse color value: '{0}'", value);
            throw new InvalidContentException(message, identity, exception);
              }
        }
        /*static*/
        private void OptimizeForCache(IList<Vector3F> positions, IList<int> indices, ContentIdentity identity)
        {
            Debug.Assert(positions != null);
              Debug.Assert(indices != null);

              var originalPositions = positions.ToArray();
              int numberOfVertices = originalPositions.Length;

              int[] vertexRemap;
              int[] duplicateVertices;
              OptimizeForCache(positions, indices, out vertexRemap, out duplicateVertices, identity);

              positions.Clear();
              for (int i = 0; i < vertexRemap.Length; i++)
              {
            if (vertexRemap[i] != -1)
              positions.Add(new Vector3F());
              }

              // Add reordered vertices.
              for (int oldIndex = 0; oldIndex < numberOfVertices; oldIndex++)
              {
            int newIndex = vertexRemap[oldIndex];
            if (newIndex != -1)
              positions[newIndex] = originalPositions[oldIndex];
              }

              Debug.Assert(vertexRemap.Length == numberOfVertices + duplicateVertices.Length);

              // Add duplicate vertices.
              for (int i = 0; i < duplicateVertices.Length; i++)
              {
            int newIndex = vertexRemap[numberOfVertices + i];
            if (newIndex != -1)
              positions[newIndex] = originalPositions[duplicateVertices[i]];
              }
        }
Ejemplo n.º 40
0
        public static void Split(AnimationContentDictionary animationDictionary, IList<AnimationSplitDefinition> splits, ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            if (splits == null || splits.Count == 0)
            return;

              if (animationDictionary == null)
            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!");

              // Get first animation.
              var originalAnimation = animationDictionary.First().Value;

              // Clear animation dictionary. - We do not keep the original animations!
              animationDictionary.Clear();

              // Add an animation to animationDictionary for each split.
              foreach (var split in splits)
              {
            TimeSpan startTime = split.StartTime;
            TimeSpan endTime = split.EndTime;

            var newAnimation = new AnimationContent
            {
              Name = split.Name,
              Duration = endTime - startTime
            };

            // Process all channels.
            foreach (var item in originalAnimation.Channels)
            {
              string channelName = item.Key;
              AnimationChannel originalChannel = item.Value;
              if (originalChannel.Count == 0)
            return;

              AnimationChannel newChannel = new AnimationChannel();

              // Add all key frames to the channel that are in the split interval.
              foreach (AnimationKeyframe keyFrame in originalChannel)
              {
            TimeSpan time = keyFrame.Time;
            if (startTime <= time && time <= endTime)
            {
              newChannel.Add(new AnimationKeyframe(keyFrame.Time - startTime, keyFrame.Transform));
            }
              }

              // Add channel if it contains key frames.
              if (newChannel.Count > 0)
            newAnimation.Channels.Add(channelName, newChannel);
            }

            if (newAnimation.Channels.Count == 0)
            {
              var message = string.Format(CultureInfo.InvariantCulture, "The split animation '{0}' is empty.", split.Name);
              throw new InvalidContentException(message, contentIdentity);
            }

            if (animationDictionary.ContainsKey(split.Name))
            {
              var message = string.Format(CultureInfo.InvariantCulture, "Cannot add split animation '{0}' because an animation with the same name already exits.", split.Name);
              throw new InvalidContentException(message, contentIdentity);
            }

            animationDictionary.Add(split.Name, newAnimation);
              }
        }
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContentDictionary
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Dictionary<string, AnimationClip> ProcessAnimations(
            AnimationContentDictionary animations, IList<BoneContent> bones,
            ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // Build up a table mapping bone names to indices.
            Dictionary<string, int> boneMap = new Dictionary<string, int>();

            for (int i = 0; i < bones.Count; i++)
            {
                string boneName = bones[i].Name;

                if (!string.IsNullOrEmpty(boneName))
                    boneMap.Add(boneName, i);
            }

            // Convert each animation in turn.
            Dictionary<string, AnimationClip> animationClips;
            animationClips = new Dictionary<string, AnimationClip>();

            foreach (KeyValuePair<string, AnimationContent> animation in animations)
            {
                AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key);

                animationClips.Add(animation.Key, processed);
            }

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                context.AddDependency(AnimPath);

                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                //breaks up original animation clips into new clips
                if (animationClips.ContainsKey(AnimDef.originalClipName))
                {
                    //graps main clip
                    AnimationClip MainClip = animationClips[AnimDef.originalClipName];
                    //remove original clip from animations
                    animationClips.Remove(AnimDef.originalClipName);

                    foreach (AnimationDefinition.clipPart Part in AnimDef.ClipParts)
                    {
                        //calculate frame times
                        TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks);
                        TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks);

                        //get keyframes for animation clip thats in start and end time
                        List<Keyframe> Keyframes = new List<Keyframe>();
                        foreach (Keyframe AnimFrame in MainClip.Keyframes)
                        {
                            if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime))
                            {
                                Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform);
                                Keyframes.Add(NewFrame);
                            }
                        }

                        //process events
                        /*List<AnimationEvent> Events = new List<AnimationEvent>();
                        if (Part.Events != null)
                        {
                            foreach (AnimationDefinition.clipPart.Event Event in Part.Events)
                            {
                                TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.originalFrameCount, MainClip.Duration.Ticks);
                                EventTime -= StartTime;

                                AnimationEvent newEvent = new AnimationEvent();
                                newEvent.EventTime = EventTime;
                                newEvent.EventName = Event.Name;
                                Events.Add(newEvent);
                            }
                        }*/

                        AnimationClip newClip = new AnimationClip(EndTime - StartTime, Keyframes, Part.ClipName);
                        animationClips[Part.ClipName] = newClip;
                    }
                }
            }

               /* if (animationClips.Count == 0)
            {
                throw new InvalidContentException(
                            "Input file does not contain any animations.");
            }*/

            return animationClips;
        }
Ejemplo n.º 42
0
        /// <summary>
        /// Recursive function adds vegetation billboards to all meshes.
        /// </summary>
        void GenerateVegetation(NodeContent node, ContentIdentity identity)
        {
            // First, recurse over any child nodes.
            foreach (NodeContent child in node.Children)
            {
                GenerateVegetation(child, identity);
            }

            // Check whether this node is in fact a mesh.
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                // Create three new geometry objects, one for each type
                // of billboard that we are going to create. Set different
                // effect parameters to control the size and wind sensitivity
                // for each type of billboard.
                GeometryContent grass = CreateVegetationGeometry("grass.tga", 5, 5, 1, identity);
                GeometryContent trees = CreateVegetationGeometry("tree.tga", 12, 12, 0.5f, identity);
                GeometryContent cats = CreateVegetationGeometry("cat.tga", 5, 5, 0, identity);

                MeshContent vegetationMesh = new MeshContent { Name = "Billboards" };

                // Loop over all the existing geometry in this mesh.
                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    IList<int> indices = geometry.Indices;
                    IList<Vector3> positions = geometry.Vertices.Positions;
                    IList<Vector3> normals = geometry.Vertices.Channels.Get<Vector3>(VertexChannelNames.Normal());

                    // Loop over all the triangles in this piece of geometry.
                    for (int triangle = 0; triangle < indices.Count; triangle += 3)
                    {
                        // Look up the three indices for this triangle.
                        int i1 = indices[triangle];
                        int i2 = indices[triangle + 1];
                        int i3 = indices[triangle + 2];

                        // Create vegetation billboards to cover this triangle.
                        // A more sophisticated implementation would measure the
                        // size of the triangle to work out how many to create,
                        // but we don't bother since we happen to know that all
                        // our triangles are roughly the same size.
                        for (int count = 0; count < BillboardsPerTriangle; count++)
                        {
                            Vector3 position, normal;

                            // Choose a random location on the triangle.
                            PickRandomPoint(positions[i1], positions[i2], positions[i3],
                                            normals[i1], normals[i2], normals[i3],
                                            out position, out normal);

                            // Randomly choose what type of billboard to create.
                            GeometryContent billboardType;

                            if (random.NextDouble() < TreeProbability)
                            {
                                billboardType = trees;

                                // As a special case, force trees to point straight
                                // upward, even if they are growing on a slope.
                                // That's what trees do in real life, after all!
                                normal = Vector3.Up;
                            }
                            else if (random.NextDouble() < CatProbability)
                            {
                                billboardType = cats;
                            }
                            else
                            {
                                billboardType = grass;
                            }

                            // Add a new billboard to the output geometry.
                            GenerateBillboard(vegetationMesh, billboardType, position, normal);
                        }
                    }
                }

                // Add our new billboard geometry to the main mesh.
                vegetationMesh.Geometry.Add(grass);
                vegetationMesh.Geometry.Add(trees);
                vegetationMesh.Geometry.Add(cats);

                mesh.Children.Add(vegetationMesh);
            }
        }
Ejemplo n.º 43
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
#if LINUX
            var targetDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;

            try
            {
                AssimpLibrary.Instance.LoadLibrary(
                    Path.Combine(targetDir, "libassimp.so"),
                    Path.Combine(targetDir, "libassimp.so"));
            }
            catch { }
#endif

            _identity = new ContentIdentity(filename, string.IsNullOrEmpty(ImporterName) ? GetType().Name : ImporterName);

            using (var importer = new AssimpContext())
            {
                // FBXPreservePivotsConfig(false) can be set to remove transformation
                // pivots. However, Assimp does not automatically correct animations!
                // --> Leave default settings, handle transformation pivots explicitly.
                //importer.SetConfig(new Assimp.Configs.FBXPreservePivotsConfig(false));

                // Note about Assimp post-processing:
                // Keep post-processing to a minimum. The ModelImporter should import
                // the model as is. We don't want to lose any information, i.e. empty
                // nodes shoud not be thrown away, meshes/materials should not be merged,
                // etc. Custom model processors may depend on this information!
                _scene = importer.ImportFile(filename,
                                             PostProcessSteps.FindDegenerates |
                                             PostProcessSteps.FindInvalidData |
                                             PostProcessSteps.FlipUVs |          // Required for Direct3D
                                             PostProcessSteps.FlipWindingOrder | // Required for Direct3D
                                             PostProcessSteps.JoinIdenticalVertices |
                                             PostProcessSteps.ImproveCacheLocality |
                                             PostProcessSteps.OptimizeMeshes |
                                             PostProcessSteps.Triangulate

                                             // Unused:
                                             //PostProcessSteps.CalculateTangentSpace
                                             //PostProcessSteps.Debone |
                                             //PostProcessSteps.FindInstances |      // No effect + slow?
                                             //PostProcessSteps.FixInFacingNormals |
                                             //PostProcessSteps.GenerateNormals |
                                             //PostProcessSteps.GenerateSmoothNormals |
                                             //PostProcessSteps.GenerateUVCoords |
                                             //PostProcessSteps.LimitBoneWeights |
                                             //PostProcessSteps.MakeLeftHanded |     // Not necessary, XNA is right-handed.
                                             //PostProcessSteps.OptimizeGraph |      // Will eliminate helper nodes
                                             //PostProcessSteps.PreTransformVertices |
                                             //PostProcessSteps.RemoveComponent |
                                             //PostProcessSteps.RemoveRedundantMaterials |
                                             //PostProcessSteps.SortByPrimitiveType |
                                             //PostProcessSteps.SplitByBoneCount |
                                             //PostProcessSteps.SplitLargeMeshes |
                                             //PostProcessSteps.TransformUVCoords |
                                             //PostProcessSteps.ValidateDataStructure |
                                             );

                FindSkeleton();     // Find _rootBone, _bones, _deformationBones.
                ImportMaterials();  // Create _materials.
                ImportNodes();      // Create _pivots and _rootNode (incl. children).
                ImportSkeleton();   // Create skeleton (incl. animations) and add to _rootNode.

                // If we have a simple hierarchy with no bones and just the one
                // mesh, we can flatten it out so the mesh is the root node.
                if (_rootNode.Children.Count == 1 && _rootNode.Children[0] is MeshContent)
                {
                    var absXform = _rootNode.Children[0].AbsoluteTransform;
                    _rootNode           = _rootNode.Children[0];
                    _rootNode.Identity  = _identity;
                    _rootNode.Transform = absXform;
                }

                _scene.Clear();
            }

            return(_rootNode);
        }
Ejemplo n.º 44
0
        private Exception ProcessErrorsAndWarnings(string errorsAndWarnings, EffectContent input, ContentProcessorContext context)
        {
            // Split the errors by lines.
            var errors = errorsAndWarnings.Split('\n');

            // Process each error line extracting the location and message information.
            for (var i = 0; i < errors.Length; i++)
            {
                // Skip blank lines.
                if (errors[i].StartsWith(Environment.NewLine))
                    break;

                // find some unique characters in the error string
                var openIndex = errors[i].IndexOf('(');
                var closeIndex = errors[i].IndexOf(')');

                // can't process the message if it has no line counter
                if (openIndex == -1 || closeIndex == -1)
                    continue;

                // find the error number, then move forward into the message
                var errorIndex = errors[i].IndexOf('X', closeIndex);
                if (errorIndex < 0)
                    return new InvalidContentException(errors[i], input.Identity);

                // trim out the data we need to feed the logger
                var fileName = errors[i].Remove(openIndex);
                var lineAndColumn = errors[i].Substring(openIndex + 1, closeIndex - openIndex - 1);
                var description = errors[i].Substring(errorIndex);

                // when the file name is not present, the error can be found in the root file
                if (string.IsNullOrEmpty(fileName))
                    fileName = input.Identity.SourceFilename;

                // ensure that the file data points toward the correct file
                var fileInfo = new FileInfo(fileName);
                if (!fileInfo.Exists)
                {
                    var parentFile = new FileInfo(input.Identity.SourceFilename);
                    fileInfo = new FileInfo(Path.Combine(parentFile.Directory.FullName, fileName));
                }
                fileName = fileInfo.FullName;

                // construct the temporary content identity and file the error or warning
                var identity = new ContentIdentity(fileName, input.Identity.SourceTool, lineAndColumn);
                if (errors[i].Contains("warning"))
                {
                    description = "A warning was generated when compiling the effect.\n" + description;
                    context.Logger.LogWarning(string.Empty, identity, description, string.Empty);
                }
                else if (errors[i].Contains("error"))
                {
                    description = "Unable to compile the effect.\n" + description;
                    return new InvalidContentException(description, identity);
                }
            }

            // if no exceptions were created in the above loop, generate a generic one here
            return new InvalidContentException(errorsAndWarnings, input.Identity);
        }
Ejemplo n.º 45
0
 public ExternalReference(string filename, ContentIdentity relativeToContent)
 {
     Filename = filename;
 }
Ejemplo n.º 46
0
 public abstract void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, Object[] messageArgs);
Ejemplo n.º 47
0
 protected string GetCurrentFilename(ContentIdentity contentIdentity)
 {
     throw new NotImplementedException();
 }
Ejemplo n.º 48
0
        /// <summary>
        /// Gets the mandatory attribute from the specified XML element.
        /// </summary>
        /// <param name="element">The XML element.</param>
        /// <param name="name">The name of the attribute.</param>
        /// <param name="identity">The content identity.</param>
        /// <returns>The attribute value.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="element"/> or <paramref name="name"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="name"/> is empty.
        /// </exception>
        public static string GetMandatoryAttribute(this XElement element, string name, ContentIdentity identity)
        {
            if (element == null)
            throw new ArgumentNullException("element");
              if (name == null)
            throw new ArgumentNullException("name");
              if (name.Length == 0)
            throw new ArgumentException("The attribute name must not be empty.", "name");

              var attribute = element.Attribute(name);
              if (attribute == null)
              {
            string message = GetExceptionMessage(element, "\"{0}\" attribute is missing.", name);
            throw new InvalidContentException(message, identity);
              }

              string s = (string)attribute;
              if (s.Length == 0)
              {
            string message = GetExceptionMessage(element, "\"{0}\" attribute must not be empty.", name);
            throw new InvalidContentException(message, identity);
              }

              return s;
        }
Ejemplo n.º 49
0
        static internal TextureContent Import(string filename, ContentImporterContext context)
        {
            var identity = new ContentIdentity(filename);
            TextureContent output = null;

            using (var reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
            {
                // Read signature ("DDS ")
                var valid = reader.ReadByte() == 0x44;
                valid = valid && reader.ReadByte() == 0x44;
                valid = valid && reader.ReadByte() == 0x53;
                valid = valid && reader.ReadByte() == 0x20;
                if (!valid)
                    throw new ContentLoadException("Invalid file signature");

                var header = new DdsHeader();

                // Read DDS_HEADER
                header.dwSize = reader.ReadUInt32();
                if (header.dwSize != 124)
                    throw new ContentLoadException("Invalid DDS_HEADER dwSize value");
                header.dwFlags = (Ddsd)reader.ReadUInt32();
                header.dwHeight = reader.ReadUInt32();
                header.dwWidth = reader.ReadUInt32();
                header.dwPitchOrLinearSize = reader.ReadUInt32();
                header.dwDepth = reader.ReadUInt32();
                header.dwMipMapCount = reader.ReadUInt32();
                // The next 11 DWORDs are reserved and unused
                for (int i = 0; i < 11; ++i)
                    reader.ReadUInt32();
                // Read DDS_PIXELFORMAT
                header.ddspf.dwSize = reader.ReadUInt32();
                if (header.ddspf.dwSize != 32)
                    throw new ContentLoadException("Invalid DDS_PIXELFORMAT dwSize value");
                header.ddspf.dwFlags = (Ddpf)reader.ReadUInt32();
                header.ddspf.dwFourCC = (FourCC)reader.ReadUInt32();
                header.ddspf.dwRgbBitCount = reader.ReadUInt32();
                header.ddspf.dwRBitMask = reader.ReadUInt32();
                header.ddspf.dwGBitMask = reader.ReadUInt32();
                header.ddspf.dwBBitMask = reader.ReadUInt32();
                header.ddspf.dwABitMask = reader.ReadUInt32();
                // Continue reading DDS_HEADER
                header.dwCaps = (DdsCaps)reader.ReadUInt32();
                header.dwCaps2 = (DdsCaps2)reader.ReadUInt32();
                // dwCaps3 unused
                reader.ReadUInt32();
                // dwCaps4 unused
                reader.ReadUInt32();
                // dwReserved2 unused
                reader.ReadUInt32();

                // Check for the existence of the DDS_HEADER_DXT10 struct next
                if (header.ddspf.dwFlags == Ddpf.FourCC && header.ddspf.dwFourCC == FourCC.Dx10)
                {
                    throw new ContentLoadException("Unsupported DDS_HEADER_DXT10 struct found");
                }

                int faceCount = 1;
                int mipMapCount = (int)(header.dwCaps.HasFlag(DdsCaps.MipMap) ? header.dwMipMapCount : 1);
                if (header.dwCaps.HasFlag(DdsCaps.Complex))
                {
                    if (header.dwCaps2.HasFlag(DdsCaps2.Cubemap))
                    {
                        if (!header.dwCaps2.HasFlag(DdsCaps2.CubemapAllFaces))
                            throw new ContentLoadException("Incomplete cubemap in DDS file");
                        faceCount = 6;
                        output = new TextureCubeContent() { Identity = identity };
                    }
                    else
                    {
                        output = new Texture2DContent() { Identity = identity };
                    }
                }
                else
                {
                    output = new Texture2DContent() { Identity = identity };
                }

                bool rbSwap;
                var format = GetSurfaceFormat(ref header.ddspf, out rbSwap);

                for (int f = 0; f < faceCount; ++f)
                {
                    var w = (int)header.dwWidth;
                    var h = (int)header.dwHeight;
                    var mipMaps = new MipmapChain();
                    for (int m = 0; m < mipMapCount; ++m)
                    {
                        var content = CreateBitmapContent(format, w, h);
                        var byteCount = GetBitmapSize(format, w, h);
                        var bytes = reader.ReadBytes(byteCount);
                        content.SetPixelData(bytes);
                        mipMaps.Add(content);
                        w = MathHelper.Max(1, w / 2);
                        h = MathHelper.Max(1, h / 2);
                    }
                    output.Faces[f] = mipMaps;
                }
            }

            return output;
        }