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) { if (!cache.ContentFileExists(path)) { throw new FileNotFoundException("Content file does not exist for font"); } switch (GameController.Mode) { case GameController.DisplayMode.Headless: break; case GameController.DisplayMode.Godot: if (!cache.TryGetDiskFilePath(path, out string diskPath)) { throw new InvalidOperationException("Fonts can only be loaded from disk."); } var res = Godot.ResourceLoader.Load(diskPath); if (!(res is Godot.DynamicFontData fontData)) { throw new InvalidDataException("Path does not point to a font."); } FontData = fontData; break; case GameController.DisplayMode.Clyde: FontFaceHandle = IoCManager.Resolve <IFontManagerInternal>().Load(cache.ContentFileRead(path).ToArray()); break; default: throw new ArgumentOutOfRangeException(); } }
private Dictionary <string, Image> GetTileImages( ITileDefinitionManager tileDefinitionManager, IResourceCache resourceCache, int tileSize) { var stopwatch = new Stopwatch(); stopwatch.Start(); var images = new Dictionary <string, Image>(); foreach (var definition in tileDefinitionManager) { var sprite = definition.SpriteName; if (string.IsNullOrEmpty(sprite)) { continue; } using var stream = resourceCache.ContentFileRead($"{TilesPath}{sprite}.png"); Image tileImage = Image.Load <Rgba32>(stream); if (tileImage.Width != tileSize || tileImage.Height != tileSize) { throw new NotSupportedException($"Unable to use tiles with a dimension other than {tileSize}x{tileSize}."); } images[sprite] = tileImage; } Console.WriteLine($"Indexed all tile images in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); return(images); }
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 Reload(IResourceCache cache, ResourcePath path, CancellationToken ct = default) { ct = ct != default ? ct : new CancellationTokenSource(30000).Token; for (;;) { try { using var stream = cache.ContentFileRead(path); using var reader = new StreamReader(stream, EncodingHelpers.UTF8); ParsedShader = ShaderParser.Parse(reader, cache); break; } catch (IOException ioe) { if (!PathHelpers.IsFileInUse(ioe)) { throw; } ct.ThrowIfCancellationRequested(); Thread.Sleep(3); } } var clyde = IoCManager.Resolve <IClydeInternal>(); clyde.ReloadShader(ClydeHandle, ParsedShader); }
public override void Load(IResourceCache cache, ResourcePath path) { using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, EncodingHelpers.UTF8)) { Asset = GodotParser.Parse(reader); } }
private void _loadOpenGL(IResourceCache cache, ResourcePath path, TextureLoadParameters?parameters) { DebugTools.Assert(GameController.Mode == GameController.DisplayMode.Clyde); var manager = IoCManager.Resolve <IClyde>(); Texture = manager.LoadTextureFromPNGStream(cache.ContentFileRead(path), path.ToString(), parameters); }
public override void Load(IResourceCache cache, ResourcePath path) { if (!cache.ContentFileExists(path)) { throw new FileNotFoundException("Content file does not exist for font"); } FontFaceHandle = IoCManager.Resolve <IFontManagerInternal>().Load(cache.ContentFileRead(path).ToArray()); }
public override void Load(IResourceCache cache, ResourcePath path) { using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, Encoding.UTF8)) { var code = reader.ReadToEnd(); GodotShader = new Godot.Shader { Code = code, }; } }
public override void Load(IResourceCache cache, ResourcePath path) { using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, EncodingHelpers.UTF8)) { ParsedShader = ShaderParser.Parse(reader, cache); } var clyde = IoCManager.Resolve <IClydeInternal>(); ClydeHandle = clyde.LoadShader(ParsedShader, path.ToString()); }
public override void Load(IResourceCache cache, ResourcePath path) { if (!cache.ContentFileExists(path)) { throw new FileNotFoundException("Content file does not exist for audio sample."); } switch (GameController.Mode) { case GameController.DisplayMode.Headless: AudioStream = new AudioStream(); break; case GameController.DisplayMode.Godot: using (var fileStream = cache.ContentFileRead(path)) { var stream = new Godot.AudioStreamOGGVorbis() { Data = fileStream.ToArray(), }; if (stream.GetLength() == 0) { throw new InvalidDataException(); } AudioStream = new AudioStream(stream); } break; case GameController.DisplayMode.Clyde: using (var fileStream = cache.ContentFileRead(path)) { AudioStream = IoCManager.Resolve <IClyde>().LoadAudioOggVorbis(fileStream); } break; default: throw new ArgumentOutOfRangeException(); } }
public override void Load(IResourceCache cache, ResourcePath path) { using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, Encoding.UTF8)) { var code = reader.ReadToEnd(); GodotShader = new Godot.Shader { Code = code, }; } var properties = Godot.VisualServer.ShaderGetParamList(GodotShader.GetRid()); foreach (var dict in properties.Cast <IDictionary <object, object> >()) { Parameters.Add((string)dict["name"], DetectParamType(dict)); } }
private void _genTextureAtlas() { var defList = TileDefs.Where(t => !string.IsNullOrEmpty(t.SpriteName)).ToList(); const int tileSize = EyeManager.PIXELSPERMETER; var dimensionX = (int)Math.Ceiling(Math.Sqrt(defList.Count)); var dimensionY = (int)Math.Ceiling((float)defList.Count / dimensionX); var sheet = new Image <Rgba32>(dimensionX * tileSize, dimensionY * tileSize); for (var i = 0; i < defList.Count; i++) { var def = defList[i]; var column = i % dimensionX; var row = i / dimensionX; Image <Rgba32> image; using (var stream = _resourceCache.ContentFileRead($"/Textures/Tiles/{def.SpriteName}.png")) { image = Image.Load(stream); } if (image.Width != tileSize || image.Height != tileSize) { throw new NotSupportedException("Unable to use tiles with a dimension other than 32x32."); } var point = new Vector2i(column * tileSize, row * tileSize); image.Blit(new UIBox2i(0, 0, image.Width, image.Height), sheet, point); var w = (float)sheet.Width; var h = (float)sheet.Height; _tileRegions.Add(def.TileId, Box2.FromDimensions( point.X / w, (h - point.Y - EyeManager.PIXELSPERMETER) / h, tileSize / w, tileSize / h)); } TileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas"); }
public override void Load(IResourceCache cache, ResourcePath path) { using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, Encoding.UTF8)) { ParsedShader = ShaderParser.Parse(reader); } switch (GameController.Mode) { case GameController.DisplayMode.Headless: return; case GameController.DisplayMode.Godot: GodotShader = new Godot.Shader { Code = _getGodotCode(), }; break; case GameController.DisplayMode.Clyde: ClydeHandle = IoCManager.Resolve <IClyde>().LoadShader(ParsedShader); break; default: throw new ArgumentOutOfRangeException(); } if (GameController.OnGodot) { GodotShader = new Godot.Shader { Code = _getGodotCode(), }; } else { var clyde = IoCManager.Resolve <IClyde>(); // TODO: vertex shaders. ClydeHandle = clyde.LoadShader(ParsedShader, path.ToString()); } }
public override void Load(IResourceCache cache, ResourcePath path) { if (!cache.ContentFileExists(path)) { throw new FileNotFoundException("Content file does not exist for audio sample."); } using (var fileStream = cache.ContentFileRead(path)) { var stream = new Godot.AudioStreamOGGVorbis() { Data = fileStream.ToArray(), }; if (stream.GetLength() == 0) { throw new InvalidDataException(); } AudioStream = new GodotAudioStreamSource(stream); } }
private void _loadGodot(IResourceCache cache, ResourcePath path, TextureLoadParameters?parameters) { DebugTools.Assert(GameController.Mode == GameController.DisplayMode.Godot); using (var stream = cache.ContentFileRead(path)) { var buffer = stream.ToArray(); var image = new Godot.Image(); var error = image.LoadPngFromBuffer(buffer); if (error != Godot.Error.Ok) { throw new InvalidDataException($"Unable to load texture from buffer, reason: {error}"); } godotTexture = new Godot.ImageTexture(); godotTexture.CreateFromImage(image); } // Disable filter by default because pixel art. (parameters ?? TextureLoadParameters.Default).SampleParameters.ApplyToGodotTexture(godotTexture); Texture = new GodotTextureSource(godotTexture); }
private Dictionary <string, List <Image> > GetTileImages( ITileDefinitionManager tileDefinitionManager, IResourceCache resourceCache, int tileSize) { var stopwatch = new Stopwatch(); stopwatch.Start(); var images = new Dictionary <string, List <Image> >(); foreach (var definition in tileDefinitionManager) { var sprite = definition.SpriteName; images[sprite] = new List <Image>(definition.Variants); if (string.IsNullOrEmpty(sprite)) { continue; } using var stream = resourceCache.ContentFileRead($"{TilesPath}{sprite}.png"); Image tileSheet = Image.Load <Rgba32>(stream); if (tileSheet.Width != tileSize * definition.Variants || tileSheet.Height != tileSize) { throw new NotSupportedException($"Unable to use tiles with a dimension other than {tileSize}x{tileSize}."); } for (var i = 0; i < definition.Variants; i++) { var tileImage = tileSheet.Clone(o => o.Crop(new Rectangle(tileSize * i, 0, 32, 32))); images[sprite].Add(tileImage); } } Console.WriteLine($"Indexed all tile images in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); return(images); }
private void _genTextureAtlas() { var defList = TileDefs.Where(t => !string.IsNullOrEmpty(t.SpriteName)).ToList(); const int tileSize = EyeManager.PIXELSPERMETER; var dimensionX = (int)Math.Ceiling(Math.Sqrt(defList.Count)); var dimensionY = (int)Math.Ceiling((float)defList.Count / dimensionX); var sheet = new Image <Rgba32>(dimensionX * tileSize, dimensionY * tileSize); for (var i = 0; i < defList.Count; i++) { var def = defList[i]; var column = i % dimensionX; var row = i / dimensionX; Image <Rgba32> image; using (var stream = _resourceCache.ContentFileRead($"/Textures/Tiles/{def.SpriteName}.png")) { image = Image.Load(stream); } if (image.Width != tileSize || image.Height != tileSize) { throw new NotImplementedException("Unable to use tiles with a dimension other than 32x32."); } var point = new Point(column * tileSize, row * tileSize); sheet.Mutate(x => x.DrawImage(image, point, PixelColorBlendingMode.Overlay, 1)); _tileRegions.Add(def.TileId, UIBox2.FromDimensions( point.X / (float)sheet.Width, point.Y / (float)sheet.Height, tileSize / (float)sheet.Width, tileSize / (float)sheet.Height)); } TileTextureAtlas = Texture.LoadFromImage(sheet, "Tile Atlas"); }
public override void Load(IResourceCache cache, ResourcePath path) { if (!cache.ContentFileExists(path)) { throw new FileNotFoundException("Content file does not exist for audio sample."); } using (var fileStream = cache.ContentFileRead(path)) { var clyde = IoCManager.Resolve <IClydeAudio>(); if (path.Extension == "ogg") { AudioStream = clyde.LoadAudioOggVorbis(fileStream, path.ToString()); } else if (path.Extension == "wav") { AudioStream = clyde.LoadAudioWav(fileStream, path.ToString()); } else { throw new NotSupportedException("Unable to load audio files outside of ogg Vorbis or PCM wav"); } } }
private void _loadOpenGL(IResourceCache cache, ResourcePath path, TextureLoadParameters?parameters) { var manager = IoCManager.Resolve <IClyde>(); Texture = manager.LoadTextureFromPNGStream(cache.ContentFileRead(path), path.ToString(), parameters); }
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; }
private void _initWindow() { var width = _configurationManager.GetCVar <int>("display.width"); var height = _configurationManager.GetCVar <int>("display.height"); _window = new GameWindow( width, height, GraphicsMode.Default, "Space Station 14", GameWindowFlags.Default, DisplayDevice.Default, 3, 3, #if DEBUG GraphicsContextFlags.Debug | GraphicsContextFlags.ForwardCompatible #else GraphicsContextFlags.ForwardCompatible #endif ) { Visible = true }; _windowSize = new Vector2i(_window.Width, _window.Height); _mainThread = Thread.CurrentThread; _window.KeyDown += (sender, eventArgs) => { _gameController.GameController.KeyDown((KeyEventArgs)eventArgs); }; _window.KeyUp += (sender, eventArgs) => { _gameController.GameController.KeyUp((KeyEventArgs)eventArgs); }; _window.Closed += (sender, eventArgs) => { _gameController.GameController.Shutdown("Window closed"); }; _window.Resize += (sender, eventArgs) => { var oldSize = _windowSize; _windowSize = new Vector2i(_window.Width, _window.Height); GL.Viewport(0, 0, _window.Width, _window.Height); GL.BindTexture(TextureTarget.Texture2D, LightTexture.Handle); var(lightW, lightH) = _lightMapSize(); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba16f, lightW, lightH, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero); OnWindowResized?.Invoke(new WindowResizedEventArgs(oldSize, _windowSize)); }; _window.MouseDown += (sender, eventArgs) => { var mouseButtonEventArgs = (MouseButtonEventArgs)eventArgs; _gameController.GameController.MouseDown(mouseButtonEventArgs); if (!mouseButtonEventArgs.Handled) { _gameController.GameController.KeyDown((KeyEventArgs)eventArgs); } }; _window.MouseUp += (sender, eventArgs) => { var mouseButtonEventArgs = (MouseButtonEventArgs)eventArgs; _gameController.GameController.MouseUp(mouseButtonEventArgs); if (!mouseButtonEventArgs.Handled) { _gameController.GameController.KeyUp((KeyEventArgs)eventArgs); } }; _window.MouseMove += (sender, eventArgs) => { MouseScreenPosition = new Vector2(eventArgs.X, eventArgs.Y); _gameController.GameController.MouseMove((MouseMoveEventArgs)eventArgs); }; _window.MouseWheel += (sender, eventArgs) => { _gameController.GameController.MouseWheel((MouseWheelEventArgs)eventArgs); }; _window.KeyPress += (sender, eventArgs) => { // If this is a surrogate it has to be specifically handled and I'm not doing that yet. DebugTools.Assert(!char.IsSurrogate(eventArgs.KeyChar)); _gameController.GameController.TextEntered(new TextEventArgs(eventArgs.KeyChar)); }; using (var iconFile = _resourceCache.ContentFileRead("/Textures/Logo/icon.ico")) { _window.Icon = new Icon(iconFile); } _initOpenGL(); }
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; }
private ShaderProgram _compileProgram(ResourcePath vertex, ResourcePath fragment, string name = null) { string vertexSource; string fragmentSource; using (var vertexReader = new StreamReader(_resourceCache.ContentFileRead(vertex), Encoding.UTF8)) { vertexSource = vertexReader.ReadToEnd(); } using (var fragmentReader = new StreamReader(_resourceCache.ContentFileRead(fragment), Encoding.UTF8)) { fragmentSource = fragmentReader.ReadToEnd(); } Shader vertexShader = null; Shader fragmentShader = null; try { try { vertexShader = new Shader(this, ShaderType.VertexShader, vertexSource, vertex.ToString()); } catch (ShaderCompilationException e) { throw new ShaderCompilationException( $"Failed to compile vertex shader {vertex}, see inner for details.", e); } try { fragmentShader = new Shader(this, ShaderType.FragmentShader, fragmentSource, fragment.ToString()); } catch (ShaderCompilationException e) { throw new ShaderCompilationException( $"Failed to compile fragment shader {fragment}, see inner for details.", e); } var program = new ShaderProgram(this, name); program.Add(vertexShader); program.Add(fragmentShader); try { program.Link(); } catch (ShaderCompilationException e) { program.Delete(); throw new ShaderCompilationException( $"Failed to link shaders. vert: {vertex}, frag: {fragment}, see inner for details.", e); } return(program); } finally { vertexShader?.Delete(); fragmentShader?.Delete(); } }
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][];