internal static void LoadPreTexture(IResourceCache cache, LoadStepData data) { var metadata = LoadRsiMetadata(cache, data.Path); var stateCount = metadata.States.Length; var toAtlas = new StateReg[stateCount]; var frameSize = metadata.Size; var rsi = new RSI(frameSize, data.Path, metadata.States.Length); var callbackOffsets = new Dictionary <RSI.StateId, Vector2i[][]>(stateCount); // Check for duplicate states for (var i = 0; i < metadata.States.Length; i++) { var stateId = metadata.States[i].StateId; for (int j = i + 1; j < metadata.States.Length; j++) { if (stateId == metadata.States[j].StateId) { throw new RSILoadException($"RSI '{data.Path}' has a duplicate stateId '{stateId}'."); } } } // Do every state. for (var index = 0; index < metadata.States.Length; index++) { ref var reg = ref toAtlas[index]; var stateObject = metadata.States[index]; // Load image from disk. var texPath = data.Path / (stateObject.StateId + ".png"); using (var stream = cache.ContentFileRead(texPath)) { reg.Src = Image.Load <Rgba32>(stream); } if (reg.Src.Width % frameSize.X != 0 || reg.Src.Height % frameSize.Y != 0) { var regDims = $"{reg.Src.Width}x{reg.Src.Height}"; var iconDims = $"{frameSize.X}x{frameSize.Y}"; throw new RSILoadException($"State '{stateObject.StateId}' image size ({regDims}) is not a multiple of the icon size ({iconDims})."); } // Load all frames into a list so we can operate on it more sanely. reg.TotalFrameCount = stateObject.Delays.Sum(delayList => delayList.Length); var(foldedDelays, foldedIndices) = FoldDelays(stateObject.Delays); var textures = new Texture[foldedIndices.Length][]; var callbackOffset = new Vector2i[foldedIndices.Length][]; for (var i = 0; i < textures.Length; i++) { textures[i] = new Texture[foldedIndices[0].Length]; callbackOffset[i] = new Vector2i[foldedIndices[0].Length]; } reg.Output = textures; reg.Indices = foldedIndices; reg.Offsets = callbackOffset; var state = new RSI.State(frameSize, rsi, stateObject.StateId, stateObject.DirType, foldedDelays, textures); rsi.AddState(state); callbackOffsets[stateObject.StateId] = callbackOffset; }
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; }