public override void Load(IResourceCache cache, ResourcePath path) { var manifestPath = path / "meta.json"; string manifestContents; using (var manifestFile = cache.ContentFileRead(manifestPath)) using (var reader = new StreamReader(manifestFile)) { manifestContents = reader.ReadToEnd(); } #if DEBUG if (RSISchema != null) { var errors = RSISchema.Validate(manifestContents); if (errors.Count != 0) { Logger.Error($"Unable to load RSI from '{path}', {errors.Count} errors:"); foreach (var error in errors) { Logger.Error("{0}", error.ToString()); } throw new RSILoadException($"{errors.Count} errors while loading RSI. See console."); } } #endif // Ok schema validated just fine. var manifestJson = JObject.Parse(manifestContents); var toAtlas = new List <(Image <Rgba32> src, Texture[][] output, int[][] indices, Vector2i[][] offsets, int totalFrameCount)>();
public override void Load(IResourceCache cache, ResourcePath path) { var manifestPath = path / "meta.json"; string manifestContents; using (var manifestFile = cache.ContentFileRead(manifestPath)) using (var reader = new StreamReader(manifestFile)) { manifestContents = reader.ReadToEnd(); } if (RSISchema != null) { var errors = RSISchema.Validate(manifestContents); if (errors.Count != 0) { Logger.Error($"Unable to load RSI from '{path}', {errors.Count} errors:"); foreach (var error in errors) { Logger.Error("{0}", error.ToString()); } throw new RSILoadException($"{errors.Count} errors while loading RSI. See console."); } } // Ok schema validated just fine. var manifestJson = JObject.Parse(manifestContents); var size = manifestJson["size"].ToObject <Vector2u>(); var rsi = new RSI(size); var images = new List <Image <Rgba32> >(); var directionFramesList = new List <(Texture, float)[]>();
public override void Load(IResourceCache cache, ResourcePath path) { var manifestPath = path / "meta.json"; string manifestContents; using (var manifestFile = cache.ContentFileRead(manifestPath)) using (var reader = new StreamReader(manifestFile)) { manifestContents = reader.ReadToEnd(); } if (RSISchema != null) { var errors = RSISchema.Validate(manifestContents); if (errors.Count != 0) { Logger.Error($"Unable to load RSI from '{path}', {errors.Count} errors:"); foreach (var error in errors) { Logger.Error("{0}", error.ToString()); } throw new RSILoadException($"{errors.Count} errors while loading RSI. See console."); } } // Ok schema validated just fine. var manifestJson = JObject.Parse(manifestContents); var size = manifestJson["size"].ToObject <Vector2u>(); var rsi = new RSI(size); // Do every state. foreach (var stateObject in manifestJson["states"].Cast <JObject>()) { var stateName = stateObject["name"].ToObject <string>(); var dirValue = stateObject["directions"].ToObject <int>(); RSI.State.DirectionType directions; switch (dirValue) { case 1: directions = RSI.State.DirectionType.Dir1; break; case 4: directions = RSI.State.DirectionType.Dir4; break; default: throw new RSILoadException($"Invalid direction: {dirValue}"); } // We can ignore selectors and flags for now, // because they're not used yet! // Get the lists of delays. float[][] delays; if (stateObject.TryGetValue("delays", out var delayToken)) { delays = delayToken.ToObject <float[][]>(); if (delays.Length != dirValue) { throw new RSILoadException($"Directions count does not match amount of delays specified."); } for (var i = 0; i < delays.Length; i++) { var delayList = delays[i]; if (delayList.Length == 0) { delays[i] = new float[] { 1 }; } } } else { delays = new float[dirValue][]; // No delays specified, default to 1 frame per dir. for (var i = 0; i < dirValue; i++) { delays[i] = new float[] { 1 }; } } var texPath = path / (stateName + ".png"); var texture = cache.GetResource <TextureResource>(texPath).Texture; if (texture.Width % size.X != 0 || texture.Height % size.Y != 0) { throw new RSILoadException("State image size is not a multiple of the icon size."); } // Amount of icons per row of the sprite sheet. var sheetWidth = texture.Width / size.X; var iconFrames = new (Texture, float)[dirValue][];
public override void Load(IResourceCache cache, ResourcePath path) { var manifestPath = path / "meta.json"; string manifestContents; using (var manifestFile = cache.ContentFileRead(manifestPath)) using (var reader = new StreamReader(manifestFile)) { manifestContents = reader.ReadToEnd(); } #if DEBUG if (RSISchema != null) { var errors = RSISchema.Validate(manifestContents); if (errors.Count != 0) { Logger.Error($"Unable to load RSI from '{path}', {errors.Count} errors:"); foreach (var error in errors) { Logger.Error("{0}", error.ToString()); } throw new RSILoadException($"{errors.Count} errors while loading RSI. See console."); } } #endif // Ok schema validated just fine. var manifestJson = JObject.Parse(manifestContents); var toAtlas = new List <(Image <Rgba32> src, Texture[][] output, int[][] indices, int totalFrameCount)>(); var metaData = ParseMetaData(manifestJson); var frameSize = metaData.Size; var rsi = new RSI(frameSize); // Do every state. foreach (var stateObject in metaData.States) { // Load image from disk. var texPath = path / (stateObject.StateId + ".png"); var image = Image.Load(cache.ContentFileRead(texPath)); var sheetSize = new Vector2i(image.Width, image.Height); if (sheetSize.X % frameSize.X != 0 || sheetSize.Y % frameSize.Y != 0) { throw new RSILoadException("State image size is not a multiple of the icon size."); } // Load all frames into a list so we can operate on it more sanely. var frameCount = stateObject.Delays.Sum(delayList => delayList.Length); var(foldedDelays, foldedIndices) = FoldDelays(stateObject.Delays); var textures = new Texture[foldedIndices.Length][]; for (var i = 0; i < textures.Length; i++) { textures[i] = new Texture[foldedIndices[0].Length]; } var state = new RSI.State(frameSize, stateObject.StateId, stateObject.DirType, foldedDelays, textures); rsi.AddState(state); toAtlas.Add((image, textures, foldedIndices, frameCount)); } // Poorly hacked in texture atlas support here. { var totalFrameCount = toAtlas.Sum(p => p.totalFrameCount); // Generate atlas. var dimensionX = (int)MathF.Ceiling(MathF.Sqrt(totalFrameCount)); var dimensionY = (int)MathF.Ceiling((float)totalFrameCount / dimensionX); using var sheet = new Image <Rgba32>(dimensionX * frameSize.X, dimensionY * frameSize.Y); var sheetIndex = 0; foreach (var(src, _, _, frameCount) in toAtlas) { // Blit all the frames over. for (var i = 0; i < frameCount; i++) { var srcWidth = (src.Width / frameSize.X); var srcColumn = i % srcWidth; var srcRow = i / srcWidth; var srcPos = (srcColumn * frameSize.X, srcRow *frameSize.Y); var sheetColumn = (sheetIndex + i) % dimensionX; var sheetRow = (sheetIndex + i) / dimensionX; var sheetPos = (sheetColumn * frameSize.X, sheetRow *frameSize.Y); var srcBox = UIBox2i.FromDimensions(srcPos, frameSize); src.Blit(srcBox, sheet, sheetPos); } sheetIndex += frameCount; } // Load atlas. var texture = Texture.LoadFromImage(sheet, path.ToString()); var sheetOffset = 0; foreach (var(src, output, indices, frameCount) in toAtlas) { for (var i = 0; i < indices.Length; i++) { var dirIndices = indices[i]; var dirOutput = output[i]; for (var j = 0; j < dirIndices.Length; j++) { var index = sheetOffset + dirIndices[j]; var sheetColumn = index % dimensionX; var sheetRow = index / dimensionX; var sheetPos = (sheetColumn * frameSize.X, sheetRow *frameSize.Y); dirOutput[j] = new AtlasTexture(texture, UIBox2.FromDimensions(sheetPos, frameSize)); } } sheetOffset += frameCount; } } foreach (var(image, _, _, _) in toAtlas) { image.Dispose(); } RSI = rsi; }