Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added content building classes.

Added solution for windows content pipeline.
  • Loading branch information...
commit 75106f88346b8326c810fda7dbe756a84df39676 1 parent 345cddf
@tomspilman tomspilman authored
View
26 MonoGame.Framework.Content.Pipeline.Windows.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Framework.Content.Pipeline.Windows", "MonoGame.Framework.Content.Pipeline\MonoGame.Framework.Content.Pipeline.Windows.csproj", "{B950DE10-AC5D-4BD9-B817-51247C4A732D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Debug|x86.ActiveCfg = Debug|x86
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Debug|x86.Build.0 = Debug|x86
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Release|x86.ActiveCfg = Release|x86
+ {B950DE10-AC5D-4BD9-B817-51247C4A732D}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
View
60 MonoGame.Framework.Content.Pipeline/Builder/PathHelper.cs
@@ -0,0 +1,60 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.IO;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public static class PathHelper
+ {
+ /// <summary>
+ /// Returns a path string normalized to the/universal/standard.
+ /// </summary>
+ public static string Normalize(string path)
+ {
+ return path.Replace('\\', '/');
+ }
+
+ /// <summary>
+ /// Returns a path string normalized to the\Windows\standard.
+ /// </summary>
+ public static string NormalizeWindows(string path)
+ {
+ return path.Replace('/', '\\');
+ }
+
+ /// <summary>
+ /// Returns a path string normalized to the current platform standard.
+ /// </summary>
+ public static string NormalizeOS(string path)
+ {
+#if WINRT
+ return NormalizeWindows(path);
+#else
+ path = path.Replace('\\', Path.DirectorySeparatorChar);
+ path = path.Replace('/', Path.DirectorySeparatorChar);
+ return path;
+#endif
+ }
+
+ /// <summary>
+ /// Returns a path relative to the base path.
+ /// </summary>
+ /// <param name="basePath">The path to make relative to. Must end with directory seperator.</param>
+ /// <param name="path">The path to be made relative to the basePath.</param>
+ /// <returns>The relative path or the original string if it is not absolute or cannot be made relative.</returns>
+ public static string GetRelativePath(string basePath, string path)
+ {
+ Uri uri;
+ if (!Uri.TryCreate(path, UriKind.Absolute, out uri))
+ return path;
+
+ uri = new Uri(basePath).MakeRelativeUri(uri);
+ var str = Uri.UnescapeDataString(uri.ToString());
+
+ return NormalizeOS(str);
+ }
+ }
+}
View
163 MonoGame.Framework.Content.Pipeline/Builder/PipelineBuildEvent.cs
@@ -0,0 +1,163 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+using System.Xml.Serialization;
+using Microsoft.Xna.Framework.Content.Pipeline;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public class PipelineBuildEvent
+ {
+ public PipelineBuildEvent()
+ {
+ SourceFile = string.Empty;
+ DestFile = string.Empty;
+ Importer = string.Empty;
+ Processor = string.Empty;
+ Parameters = new OpaqueDataDictionary();
+ ParametersXML = new List<Pair>();
+ Dependancies = new List<string>();
+ BuildAsset = new List<string>();
+ }
+
+ public string SourceFile { get; set; }
+
+ public string DestFile { get; set; }
+
+ public DateTime DestTime { get; set; }
+
+ public string Importer { get; set; }
+
+ public string Processor { get; set; }
+
+ [XmlIgnore]
+ public OpaqueDataDictionary Parameters { get; set; }
+
+ public class Pair
+ {
+ public string Key { get; set; }
+ public string Value { get; set; }
+ }
+
+ [XmlElement("Parameters")]
+ public List<Pair> ParametersXML { get; set; }
+
+ public List<string> Dependancies { get; set; }
+
+ public List<string> BuildAsset { get; set; }
+
+ public static PipelineBuildEvent Load(string filePath)
+ {
+ var deserializer = new XmlSerializer(typeof (PipelineBuildEvent));
+ PipelineBuildEvent pipelineEvent;
+ try
+ {
+ using (var textReader = new StreamReader(filePath))
+ pipelineEvent = (PipelineBuildEvent) deserializer.Deserialize(textReader);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+
+ // Repopulate the parameters from the serialized state.
+ foreach (var pair in pipelineEvent.ParametersXML)
+ pipelineEvent.Parameters.Add(pair.Key, pair.Value);
+ pipelineEvent.ParametersXML.Clear();
+
+ return pipelineEvent;
+ }
+
+ public void Save(string filePath)
+ {
+ // Make sure the directory exists.
+ Directory.CreateDirectory(Path.GetDirectoryName(filePath) + @"\");
+
+ // Convert the parameters into something we can serialize.
+ ParametersXML.Clear();
+ foreach (var pair in Parameters)
+ {
+ var converter = TypeDescriptor.GetConverter(pair.Value.GetType());
+ ParametersXML.Add(new Pair { Key = pair.Key, Value = converter.ConvertToString(pair.Value) });
+ }
+
+ // Serialize our state.
+ var serializer = new XmlSerializer(typeof (PipelineBuildEvent));
+ using (var textWriter = new StreamWriter(filePath, false, new UTF8Encoding(false)))
+ serializer.Serialize(textWriter, this);
+ }
+
+ public bool NeedsRebuild(PipelineBuildEvent cachedEvent)
+ {
+ // If we have no previously cached build event then we cannot
+ // be sure that the state hasn't changed... force a rebuild.
+ if (cachedEvent == null)
+ return true;
+
+ // Verify that the last write time of the dest file matches
+ // what we recorded when it was built. If it is different
+ // that means someone modified it and we need to rebuild.
+ var destWriteTime = File.GetLastWriteTime(DestFile);
+ if (cachedEvent.DestTime != destWriteTime)
+ return true;
+
+ // If the source and dest files changed... this is always a rebuild.
+ if (File.GetLastWriteTime(SourceFile) >= destWriteTime)
+ return true;
+
+ // Are any of the dependancy files newer than the dest file?
+ foreach (var depFile in cachedEvent.Dependancies)
+ {
+ if (File.GetLastWriteTime(depFile) >= destWriteTime)
+ return true;
+ }
+
+ // This shouldn't happen... but if the source or dest files changed
+ // then force a rebuild.
+ if (cachedEvent.SourceFile != SourceFile ||
+ cachedEvent.DestFile != DestFile)
+ return true;
+
+ // Did the importer change?
+ // TODO: I need to test the assembly versions here!
+ if (cachedEvent.Importer != Importer)
+ return true;
+
+ // Did the processor change?
+ // TODO: I need to test the assembly versions here!
+ if (cachedEvent.Processor != Processor)
+ return true;
+
+ // Finally did any of the processor parameters change?
+ foreach (var pair in cachedEvent.Parameters)
+ {
+ // If the key value doesn't exist... then rebuild.
+ object value;
+ if (!Parameters.TryGetValue(pair.Key, out value))
+ return true;
+
+ // If the values are the same type and do not match... rebuild.
+ if (value.GetType().IsInstanceOfType(pair.Value))
+ {
+ if (!value.Equals(pair.Value))
+ return true;
+ }
+ else
+ {
+ var typeConverter = TypeDescriptor.GetConverter(value.GetType());
+ var converted = typeConverter.ConvertTo(value, pair.Value.GetType());
+ if (!converted.Equals(pair.Value))
+ return true;
+ }
+ }
+
+ return false;
+ }
+ };
+}
View
23 MonoGame.Framework.Content.Pipeline/Builder/PipelineBuildLogger.cs
@@ -0,0 +1,23 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using Microsoft.Xna.Framework.Content.Pipeline;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public class PipelineBuildLogger : ContentBuildLogger
+ {
+ public override void LogMessage(string message, params object[] messageArgs)
+ {
+ }
+
+ public override void LogImportantMessage(string message, params object[] messageArgs)
+ {
+ }
+
+ public override void LogWarning(string helpLink, ContentIdentity contentIdentity, string message, params object[] messageArgs)
+ {
+ }
+ }
+}
View
26 MonoGame.Framework.Content.Pipeline/Builder/PipelineImporterContext.cs
@@ -0,0 +1,26 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using Microsoft.Xna.Framework.Content.Pipeline;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public class PipelineImporterContext : ContentImporterContext
+ {
+ private readonly PipelineManager _manager;
+
+ public PipelineImporterContext(PipelineManager manager)
+ {
+ _manager = manager;
+ }
+
+ public override string IntermediateDirectory { get { return _manager.IntermediateDirectory; } }
+ public override string OutputDirectory { get { return _manager.OutputDirectory; } }
+ public override ContentBuildLogger Logger { get { return _manager.Logger; } }
+
+ public override void AddDependency(string filename)
+ {
+ }
+ }
+}
View
355 MonoGame.Framework.Content.Pipeline/Builder/PipelineManager.cs
@@ -0,0 +1,355 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
+using Microsoft.Xna.Framework.Graphics;
+using System.Globalization;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public class PipelineManager
+ {
+ private struct ImporterInfo
+ {
+ public ContentImporterAttribute attribue;
+ public Type type;
+ };
+
+ private List<ImporterInfo> _importers;
+
+ private struct ProcessorInfo
+ {
+ public ContentProcessorAttribute attribue;
+ public Type type;
+ };
+
+ private List<ProcessorInfo> _processors;
+
+ private List<Type> _writers;
+
+ public string ProjectDirectory { get; private set; }
+ public string OutputDirectory { get; private set; }
+ public string IntermediateDirectory { get; private set; }
+
+ private ContentCompiler _compiler;
+ private MethodInfo _compileMethod;
+
+ public PipelineBuildLogger Logger { get; private set; }
+
+ public List<string> Assemblies { get; private set; }
+
+ public PipelineManager(string projectDir, string outputDir, string intermediateDir)
+ {
+ Assemblies = new List<string>();
+ Logger = new PipelineBuildLogger();
+
+ ProjectDirectory = projectDir + @"\";
+ OutputDirectory = outputDir + @"\";
+ IntermediateDirectory = intermediateDir + @"\";
+ }
+
+ public void AddAssembly(string fileName)
+ {
+ if (Assemblies.Contains(fileName))
+ return;
+
+ Assemblies.Add(fileName);
+ }
+
+ private void ResolveAssemblies()
+ {
+ _importers = new List<ImporterInfo>();
+ _processors = new List<ProcessorInfo>();
+ _writers = new List<Type>();
+
+ foreach (var assemblyName in Assemblies)
+ {
+ Type[] exportedTypes;
+ try
+ {
+ var a = Assembly.LoadFrom(assemblyName);
+ exportedTypes = a.GetExportedTypes();
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+
+ foreach (var t in exportedTypes)
+ {
+ if (!t.IsPublic || t.IsAbstract)
+ continue;
+
+ if (t.GetInterface(@"IContentImporter") != null)
+ {
+ var attributes = t.GetCustomAttributes(typeof (ContentImporterAttribute), false);
+ if (attributes.Length != 0)
+ {
+ var importerAttribute = attributes[0] as ContentImporterAttribute;
+ _importers.Add(new ImporterInfo { attribue = importerAttribute, type = t });
+ }
+ else
+ {
+ // If no attribute specify default one
+ var importerAttribute = new ContentImporterAttribute(".*");
+ importerAttribute.DefaultProcessor = "";
+ importerAttribute.DisplayName = t.Name;
+ _importers.Add(new ImporterInfo { attribue = importerAttribute, type = t });
+ }
+ }
+ else if (t.GetInterface(@"IContentProcessor") != null)
+ {
+ var attributes = t.GetCustomAttributes(typeof (ContentProcessorAttribute), false);
+ if (attributes.Length != 0)
+ {
+ var processorAttribute = attributes[0] as ContentProcessorAttribute;
+ _processors.Add(new ProcessorInfo {attribue = processorAttribute, type = t});
+ }
+ }
+ else if (t.GetInterface(@"ContentTypeWriter") != null)
+ {
+ // TODO: This doesn't work... how do i find these?
+ _writers.Add(t);
+ }
+ }
+ }
+ }
+
+ public IContentImporter CreateImporter(string name)
+ {
+ if (_importers == null)
+ ResolveAssemblies();
+
+ // Search for the importer.
+ foreach (var info in _importers)
+ {
+ if (info.type.Name.Equals(name))
+ return Activator.CreateInstance(info.type) as IContentImporter;
+ }
+
+ return null;
+ }
+
+ public string FindImporterByExtension(string ext)
+ {
+ if (_importers == null)
+ ResolveAssemblies();
+
+ // Search for the importer.
+ foreach (var info in _importers)
+ {
+ if (info.attribue.FileExtensions.Contains(ext))
+ return info.type.Name;
+ }
+
+ return null;
+ }
+
+ public string FindDefaultProcessor(string importer)
+ {
+ if (_importers == null)
+ ResolveAssemblies();
+
+ // Search for the importer.
+ foreach (var info in _importers)
+ {
+ if (info.type.Name == importer)
+ return info.attribue.DefaultProcessor;
+ }
+
+ return null;
+ }
+
+ public IContentProcessor CreateProcessor(string name, OpaqueDataDictionary processorParameters)
+ {
+ if (_processors == null)
+ ResolveAssemblies();
+
+ // Search for the processor.
+ IContentProcessor processor = null;
+ foreach (var info in _processors)
+ {
+ if (info.type.Name.Equals(name))
+ {
+ processor = (IContentProcessor)Activator.CreateInstance(info.type);
+ break;
+ }
+ }
+
+ // No processor found... exception?
+ if (processor == null)
+ return null;
+
+ // Convert and set the parameters on the processor.
+ var processorType = processor.GetType();
+ foreach (var param in processorParameters)
+ {
+ var propInfo = processorType.GetProperty(param.Key, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
+ if (propInfo == null || propInfo.GetSetMethod(false) == null)
+ continue;
+
+ // If the property value is already of the correct type then set it.
+ if (propInfo.PropertyType.IsInstanceOfType(param.Value))
+ propInfo.SetValue(processor, param.Value, null);
+ else
+ {
+ // Find a type converter for this property.
+ var typeConverter = TypeDescriptor.GetConverter(propInfo.PropertyType);
+ if (typeConverter.CanConvertFrom(param.Value.GetType()))
+ {
+ var propValue = typeConverter.ConvertFrom(null, CultureInfo.InvariantCulture, param.Value);
+ propInfo.SetValue(processor, propValue, null);
+ }
+ }
+ }
+
+ return processor;
+ }
+
+ public PipelineBuildEvent BuildContent(string sourceFilepath, string outputFilepath = null, string importerName = null, string processorName = null, OpaqueDataDictionary processorParameters = null)
+ {
+ // If the output path is null... build it from the source file path.
+ if (string.IsNullOrEmpty(outputFilepath))
+ {
+ var filename = Path.GetFileNameWithoutExtension(sourceFilepath) + ".xnb";
+ var directory = PathHelper.GetRelativePath(ProjectDirectory,
+ Path.GetDirectoryName(sourceFilepath) +
+ Path.DirectorySeparatorChar);
+ outputFilepath = Path.Combine(OutputDirectory, directory, filename);
+ }
+ else
+ {
+ // If the extension is not XNB or the source file extension then add XNB.
+ var sourceExt = Path.GetExtension(sourceFilepath);
+ if (outputFilepath.EndsWith(sourceExt, StringComparison.InvariantCultureIgnoreCase))
+ outputFilepath = outputFilepath.Substring(0, outputFilepath.Length - sourceExt.Length);
+ if (!outputFilepath.EndsWith(".xnb", StringComparison.InvariantCultureIgnoreCase))
+ outputFilepath += ".xnb";
+
+ // If the path isn't rooted then put it into the output directory.
+ if (!Path.IsPathRooted(outputFilepath))
+ outputFilepath = Path.Combine(OutputDirectory, outputFilepath);
+ }
+
+ // Resolve the importer name.
+ if (string.IsNullOrEmpty(importerName))
+ importerName = FindImporterByExtension(Path.GetExtension(sourceFilepath));
+
+ // Resolve the processor name.
+ if (string.IsNullOrEmpty(processorName))
+ processorName = FindDefaultProcessor(importerName);
+
+ // Record what we're building and how.
+ var contentEvent = new PipelineBuildEvent
+ {
+ SourceFile = sourceFilepath,
+ DestFile = outputFilepath,
+ Importer = importerName,
+ Processor = processorName,
+ Parameters = processorParameters ?? new OpaqueDataDictionary(),
+ };
+
+ // Load the previous content event if it exists.
+ var contentPath = Path.ChangeExtension(PathHelper.GetRelativePath(OutputDirectory, contentEvent.DestFile), ".content");
+ var eventFilepath = Path.Combine(IntermediateDirectory, contentPath);
+ var cachedEvent = PipelineBuildEvent.Load(eventFilepath);
+
+ BuildContent(contentEvent, cachedEvent, eventFilepath);
+
+ return contentEvent;
+ }
+
+ public void BuildContent(PipelineBuildEvent pipelineEvent, PipelineBuildEvent cachedEvent, string eventFilepath)
+ {
+ var rebuild = pipelineEvent.NeedsRebuild(cachedEvent);
+ if (!rebuild)
+ {
+ // While this asset doesn't need to be rebuilt the dependent assets might.
+ foreach (var asset in cachedEvent.BuildAsset)
+ {
+ var assetPath = Path.ChangeExtension(PathHelper.GetRelativePath(OutputDirectory, asset), ".content");
+ var assetEventFilepath = Path.Combine(IntermediateDirectory, assetPath);
+ var assetCachedEvent = PipelineBuildEvent.Load(assetEventFilepath);
+
+ // If we cannot find the cached event for the dependancy
+ // then we have to trigger a rebuild of the parent content.
+ if (assetCachedEvent == null)
+ {
+ rebuild = true;
+ break;
+ }
+
+ var depEvent = new PipelineBuildEvent
+ {
+ SourceFile = assetCachedEvent.SourceFile,
+ DestFile = assetCachedEvent.DestFile,
+ Importer = assetCachedEvent.Importer,
+ Processor = assetCachedEvent.Processor,
+ Parameters = assetCachedEvent.Parameters,
+ };
+
+ // Give the asset a chance to rebuild.
+ BuildContent(depEvent, assetCachedEvent, assetEventFilepath);
+ }
+ }
+
+ // Do we need to rebuild?
+ if (rebuild)
+ {
+ // Import the asset.
+ var importer = CreateImporter(pipelineEvent.Importer);
+ var importContext = new PipelineImporterContext(this);
+ var importedObject = importer.Import(pipelineEvent.SourceFile, importContext);
+
+ // Process the imported object.
+ var processor = CreateProcessor(pipelineEvent.Processor, pipelineEvent.Parameters);
+ var processContext = new PipelineProcessorContext(this, pipelineEvent);
+ var processedObject = processor.Process(importedObject, processContext);
+
+ // Write the content to disk.
+ WriteXnb(processedObject, pipelineEvent);
+
+ // Store the new event into the intermediate folder.
+ pipelineEvent.Save(eventFilepath);
+ }
+ }
+
+ private void WriteXnb(object content, PipelineBuildEvent pipelineEvent)
+ {
+ // Make sure the output directory exists.
+ var outputFileDir = Path.GetDirectoryName(pipelineEvent.DestFile) + @"\";
+ Directory.CreateDirectory(outputFileDir);
+
+ // TODO: For now use XNA's ContentCompiler which knows
+ // how to write XNBs for us.
+ //
+ // http://xboxforums.create.msdn.com/forums/t/72563.aspx
+ //
+ // We need to replace this with our own implementation
+ // that isn't all internal methods!
+ //
+ if (_compiler == null)
+ {
+ var ctor = typeof(ContentCompiler).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)[0];
+ _compiler = (ContentCompiler)ctor.Invoke(new object[] { });
+ _compileMethod = typeof(ContentCompiler).GetMethod("Compile", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+ }
+
+ // Write the XNB.
+ using (var stream = new FileStream(pipelineEvent.DestFile, FileMode.Create, FileAccess.Write, FileShare.None))
+ _compileMethod.Invoke(_compiler, new[] { stream, content, TargetPlatform.Windows, GraphicsProfile.Reach, false, OutputDirectory, outputFileDir });
+
+ // Store the last write time of the output XNB here
+ // so we can verify it hasn't been tampered with.
+ pipelineEvent.DestTime = File.GetLastWriteTime(pipelineEvent.DestFile);
+ }
+ }
+}
View
101 MonoGame.Framework.Content.Pipeline/Builder/PipelineProcessorContext.cs
@@ -0,0 +1,101 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System;
+using System.IO;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ public class PipelineProcessorContext : ContentProcessorContext
+ {
+ private readonly PipelineManager _manager;
+
+ private readonly PipelineBuildEvent _pipelineEvent;
+
+ public PipelineProcessorContext(PipelineManager manager, PipelineBuildEvent pipelineEvent)
+ {
+ _manager = manager;
+ _pipelineEvent = pipelineEvent;
+ }
+
+ public override TargetPlatform TargetPlatform { get { return TargetPlatform.Windows; } }
+ public override GraphicsProfile TargetProfile { get { return GraphicsProfile.Reach; } }
+
+ public override string BuildConfiguration { get { return string.Empty; } }
+
+ public override string IntermediateDirectory { get { return _manager.IntermediateDirectory; } }
+ public override string OutputDirectory { get { return _manager.OutputDirectory; } }
+ public override string OutputFilename { get { return _pipelineEvent.DestFile; } }
+
+ public override OpaqueDataDictionary Parameters { get { return _pipelineEvent.Parameters; } }
+
+ public override ContentBuildLogger Logger { get { return _manager.Logger; } }
+
+ public override void AddDependency(string filename)
+ {
+ if (!_pipelineEvent.Dependancies.Contains(filename))
+ _pipelineEvent.Dependancies.Add(filename);
+ }
+
+ public override void AddOutputFile(string filename)
+ {
+ }
+
+ public override TOutput Convert<TInput, TOutput>( TInput input,
+ string processorName,
+ OpaqueDataDictionary processorParameters)
+ {
+ var processor = _manager.CreateProcessor(processorName, processorParameters);
+ var processContext = new PipelineProcessorContext(_manager, new PipelineBuildEvent { Parameters = processorParameters } );
+ var processedObject = processor.Process(input, processContext);
+
+ // Add its dependancies and built assets to ours.
+ _pipelineEvent.Dependancies.AddRangeUnique(processContext._pipelineEvent.Dependancies);
+ _pipelineEvent.BuildAsset.AddRangeUnique(processContext._pipelineEvent.BuildAsset);
+
+ return (TOutput)processedObject;
+ }
+
+ public override TOutput BuildAndLoadAsset<TInput, TOutput>( ExternalReference<TInput> sourceAsset,
+ string processorName,
+ OpaqueDataDictionary processorParameters,
+ string importerName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override ExternalReference<TOutput> BuildAsset<TInput, TOutput>( ExternalReference<TInput> sourceAsset,
+ string processorName,
+ OpaqueDataDictionary processorParameters,
+ string importerName,
+ string assetName)
+ {
+ if (string.IsNullOrEmpty(assetName))
+ {
+ var contentPath = PathHelper.GetRelativePath(_manager.ProjectDirectory, sourceAsset.Filename);
+ var filename = Path.GetFileNameWithoutExtension(contentPath);
+ var path = Path.GetDirectoryName(contentPath);
+
+ // TODO: Is this only does for textures or
+ // for all sub-assets like this?
+ //
+ // TODO: Replace the _0 with a hex 32bit hash of
+ // the processor+parameters. This ensures no collisions
+ // when two models process textures with different settings.
+ //
+ assetName = Path.Combine(path, filename) + "_0";
+ }
+
+ // Build the content.
+ var buildEvent = _manager.BuildContent(sourceAsset.Filename, assetName, importerName, processorName, processorParameters);
+
+ // Record that we built this dependent asset.
+ _pipelineEvent.BuildAsset.AddUnique(buildEvent.DestFile);
+
+ return new ExternalReference<TOutput>(buildEvent.DestFile);
+ }
+ }
+}
View
37 MonoGame.Framework.Content.Pipeline/Builder/TypeExtensions.cs
@@ -0,0 +1,37 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ static public class TypeExtensions
+ {
+ public static Color ToColor(this System.Drawing.Color color)
+ {
+ return new Color(color.R, color.G, color.B, color.A);
+ }
+
+ public static Vector3 ToVector3(this System.Drawing.Color color)
+ {
+ return new Vector3(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f);
+ }
+
+ public static void AddUnique<T>(this List<T> list, T item)
+ {
+ if (!list.Contains(item))
+ list.Add(item);
+ }
+
+ public static void AddRangeUnique<T>(this List<T> dstList, List<T> list)
+ {
+ foreach (var i in list)
+ {
+ if (!dstList.Contains(i))
+ dstList.Add(i);
+ }
+ }
+ }
+}
View
65 MonoGame.Framework.Content.Pipeline/Builder/XmlColor.cs
@@ -0,0 +1,65 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+using System.Drawing;
+using System.Xml.Serialization;
+
+namespace MonoGame.Framework.Content.Pipeline.Builder
+{
+ /// <summary>
+ /// Helper for serializing color types with the XmlSerializer.
+ /// </summary>
+ public class XmlColor
+ {
+ private Color _color;
+
+ public XmlColor()
+ {
+ }
+
+ public XmlColor(Color c)
+ {
+ _color = c;
+ }
+
+ public static implicit operator Color(XmlColor x)
+ {
+ return x._color;
+ }
+
+ public static implicit operator XmlColor(Color c)
+ {
+ return new XmlColor(c);
+ }
+
+ public static string FromColor(Color color)
+ {
+ if (color.IsNamedColor)
+ return color.Name;
+ return string.Format("{0}, {1}, {2}, {3}", color.R, color.G, color.B, color.A);
+ }
+
+ public static Color ToColor(string value)
+ {
+ if (!value.Contains(","))
+ return Color.FromName(value);
+
+ int r, g, b, a;
+ var colors = value.Split(',');
+ int.TryParse(colors.Length > 0 ? colors[0] : string.Empty, out r);
+ int.TryParse(colors.Length > 1 ? colors[1] : string.Empty, out g);
+ int.TryParse(colors.Length > 2 ? colors[2] : string.Empty, out b);
+ int.TryParse(colors.Length > 3 ? colors[3] : string.Empty, out a);
+
+ return Color.FromArgb(a, r, g, b);
+ }
+
+ [XmlText]
+ public string Default
+ {
+ get { return FromColor(_color); }
+ set { _color = ToColor(value); }
+ }
+ }
+}
View
8 MonoGame.Framework.Content.Pipeline/MonoGame.Framework.Content.Pipeline.Windows.csproj
@@ -68,6 +68,14 @@
<Compile Include="Audio\AudioFormat.cs" />
<Compile Include="Audio\ConversionFormat.cs" />
<Compile Include="Audio\ConversionQuality.cs" />
+ <Compile Include="Builder\PathHelper.cs" />
+ <Compile Include="Builder\PipelineBuildEvent.cs" />
+ <Compile Include="Builder\PipelineBuildLogger.cs" />
+ <Compile Include="Builder\PipelineImporterContext.cs" />
+ <Compile Include="Builder\PipelineManager.cs" />
+ <Compile Include="Builder\PipelineProcessorContext.cs" />
+ <Compile Include="Builder\TypeExtensions.cs" />
+ <Compile Include="Builder\XmlColor.cs" />
<Compile Include="ChildCollection.cs" />
<Compile Include="ContentBuildLogger.cs" />
<Compile Include="ContentIdentity.cs" />
Please sign in to comment.
Something went wrong with that request. Please try again.