From 9cf8328979b5b02420bdfc802b4f58a5becd1c1e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 5 Oct 2014 15:01:45 +1300 Subject: [PATCH] Add plumbing for mod-defined sprite loaders. --- OpenRA.Game/Graphics/CursorProvider.cs | 2 +- OpenRA.Game/Graphics/SequenceProvider.cs | 2 +- OpenRA.Game/Graphics/SpriteLoader.cs | 33 ++++++++++++++++++- OpenRA.Game/Graphics/SpriteSource.cs | 14 -------- OpenRA.Game/Graphics/Theater.cs | 2 +- OpenRA.Game/Manifest.cs | 5 +++ OpenRA.Game/ModData.cs | 13 ++++++++ .../ConvertSpriteToPngCommand.cs | 2 +- 8 files changed, 54 insertions(+), 19 deletions(-) diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 042e3164d2..28701e2fd4 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -45,7 +45,7 @@ namespace OpenRA.Graphics foreach (var p in nodesDict["Palettes"].Nodes) palette.AddPalette(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false); - var spriteLoader = new SpriteLoader(new string[0], new SheetBuilder(SheetType.Indexed)); + var spriteLoader = new SpriteLoader(modData.SpriteLoaders, new string[0], new SheetBuilder(SheetType.Indexed)); foreach (var s in nodesDict["Cursors"].Nodes) LoadSequencesForCursor(spriteLoader, s.Key, s.Value); spriteLoader.SheetBuilder.Current.ReleaseBuffer(); diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs index 2f3947e137..bed18baac0 100644 --- a/OpenRA.Game/Graphics/SequenceProvider.cs +++ b/OpenRA.Game/Graphics/SequenceProvider.cs @@ -79,7 +79,7 @@ namespace OpenRA.Graphics { this.modData = modData; - spriteLoader = Exts.Lazy(() => new SpriteLoader(tileSet.Extensions, new SheetBuilder(SheetType.Indexed))); + spriteLoader = Exts.Lazy(() => new SpriteLoader(modData.SpriteLoaders, tileSet.Extensions, new SheetBuilder(SheetType.Indexed))); } public Sequences LoadSequences(Map map) diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 5414e6c7b5..4e53960f44 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -8,21 +8,44 @@ */ #endregion +using System.Drawing; +using System.IO; using System.Linq; using OpenRA.FileSystem; using OpenRA.Primitives; namespace OpenRA.Graphics { + public interface ISpriteLoader + { + bool TryParseSprite(Stream s, out ISpriteFrame[] frames); + } + + public interface ISpriteFrame + { + Size Size { get; } + Size FrameSize { get; } + float2 Offset { get; } + byte[] Data { get; } + bool DisableExportPadding { get; } + } + + public interface ISpriteSource + { + IReadOnlyList Frames { get; } + } + public class SpriteLoader { public readonly SheetBuilder SheetBuilder; + readonly ISpriteLoader[] loaders; readonly Cache sprites; readonly Cache frames; readonly string[] exts; - public SpriteLoader(string[] exts, SheetBuilder sheetBuilder) + public SpriteLoader(ISpriteLoader[] loaders, string[] exts, SheetBuilder sheetBuilder) { + this.loaders = loaders; SheetBuilder = sheetBuilder; // Include extension-less version @@ -40,8 +63,16 @@ namespace OpenRA.Graphics ISpriteFrame[] CacheFrames(string filename) { using (var stream = GlobalFileSystem.OpenWithExts(filename, exts)) + { + ISpriteFrame[] frames; + foreach (var loader in loaders) + if (loader.TryParseSprite(stream, out frames)) + return frames; + + // Fall back to the hardcoded types (for now). return SpriteSource.LoadSpriteSource(stream, filename).Frames .ToArray(); + } } public Sprite[] LoadAllSprites(string filename) { return sprites[filename]; } diff --git a/OpenRA.Game/Graphics/SpriteSource.cs b/OpenRA.Game/Graphics/SpriteSource.cs index e930660e49..80a224d226 100644 --- a/OpenRA.Game/Graphics/SpriteSource.cs +++ b/OpenRA.Game/Graphics/SpriteSource.cs @@ -14,20 +14,6 @@ using OpenRA.FileFormats; namespace OpenRA.Graphics { - public interface ISpriteFrame - { - Size Size { get; } - Size FrameSize { get; } - float2 Offset { get; } - byte[] Data { get; } - bool DisableExportPadding { get; } - } - - public interface ISpriteSource - { - IReadOnlyList Frames { get; } - } - // TODO: Most of this should be moved into the format parsers themselves. public enum SpriteType { Unknown, ShpTD, ShpTS, ShpD2, TmpTD, TmpRA, TmpTS, R8 } public static class SpriteSource diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index a5b87b04be..110b27703a 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -38,7 +38,7 @@ namespace OpenRA.Graphics templates = new Dictionary(); // We manage the SheetBuilder ourselves, to avoid loading all of the tileset images - var spriteLoader = new SpriteLoader(tileset.Extensions, null); + var spriteLoader = new SpriteLoader(Game.modData.SpriteLoaders, tileset.Extensions, null); foreach (var t in tileset.Templates) { var allFrames = spriteLoader.LoadAllFrames(t.Value.Image); diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 282ae968d1..12fc57a47f 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -37,6 +37,8 @@ namespace OpenRA public readonly Size TileSize = new Size(24, 24); public readonly TileShape TileShape = TileShape.Rectangle; + public readonly string[] SpriteFormats = { }; + [Desc("(x,y,z) offset of the full cell and each sub-cell", "X & Y should be between -512 ... 512 and Z >= 0")] public readonly WVec[] SubCellOffsets = { @@ -128,6 +130,9 @@ namespace OpenRA compat.Add(c.Trim()); MapCompatibility = compat.ToArray(); + + if (yaml.ContainsKey("SpriteFormats")) + SpriteFormats = FieldLoader.GetValue("SpriteFormats", yaml["SpriteFormats"].Value); } static string[] YamlList(Dictionary yaml, string key) diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index c5b5fc3a0c..288d9ac836 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -24,6 +24,7 @@ namespace OpenRA public readonly ObjectCreator ObjectCreator; public readonly WidgetLoader WidgetLoader; public readonly MapCache MapCache; + public readonly ISpriteLoader[] SpriteLoaders; public ILoadScreen LoadScreen = null; public VoxelLoader VoxelLoader; public readonly RulesetCache RulesetCache; @@ -45,6 +46,18 @@ namespace OpenRA RulesetCache.LoadingProgress += HandleLoadingProgress; MapCache = new MapCache(this); + var loaders = new List(); + foreach (var format in Manifest.SpriteFormats) + { + var loader = ObjectCreator.FindType(format + "Loader"); + if (loader == null || !loader.GetInterfaces().Contains(typeof(ISpriteLoader))) + throw new InvalidOperationException("Unable to find a sprite loader for type '{0}'.".F(format)); + + loaders.Add((ISpriteLoader)ObjectCreator.CreateBasic(loader)); + } + + SpriteLoaders = loaders.ToArray(); + // HACK: Mount only local folders so we have a half-working environment for the asset installer GlobalFileSystem.UnmountAll(); foreach (var dir in Manifest.Folders) diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs index 7b6d1ba2cf..f70338b257 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.UtilityCommands var palette = new ImmutablePalette(args[2], shadowIndex); - var frames = new SpriteLoader(new string[0], null) + var frames = new SpriteLoader(modData.SpriteLoaders, new string[0], null) .LoadAllFrames(src); var usePadding = !args.Contains("--nopadding");