예제 #1
0
        /// <summary>
        /// Returns the first set of sprites with the given filename.
        /// If getUsedFrames is defined then the indices returned by the function call
        /// are guaranteed to be loaded.  The value of other indices in the returned
        /// array are undefined and should never be accessed.
        /// </summary>
        public Sprite[] this[string filename, Func <int, IEnumerable <int> > getUsedFrames = null]
        {
            get
            {
                var allSprites = sprites.GetOrAdd(filename);
                var sprite     = allSprites.FirstOrDefault();

                if (!unloadedFrames.TryGetValue(filename, out var unloaded))
                {
                    unloaded = null;
                }

                // This is the first time that the file has been requested
                // Load all of the frames into the unused buffer and initialize
                // the loaded cache (initially empty)
                if (sprite == null)
                {
                    unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
                    unloadedFrames[filename] = unloaded;
                    metadata[filename]       = fileMetadata;

                    sprite = new Sprite[unloaded.Length];
                    allSprites.Add(sprite);
                }

                // HACK: The sequence code relies on side-effects from getUsedFrames
                var indices = getUsedFrames != null?getUsedFrames(sprite.Length) :
                                  Enumerable.Range(0, sprite.Length);

                // Load any unused frames into the SheetBuilder
                if (unloaded != null)
                {
                    foreach (var i in indices)
                    {
                        if (unloaded[i] != null)
                        {
                            sprite[i]   = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
                            unloaded[i] = null;
                        }
                    }

                    // All frames have been loaded
                    if (unloaded.All(f => f == null))
                    {
                        unloadedFrames.Remove(filename);
                    }
                }

                return(sprite);
            }
        }
예제 #2
0
파일: Theater.cs 프로젝트: zredb/OpenRA
        public Theater(TileSet tileset, Action <uint, string> onMissingImage = null)
        {
            this.tileset = tileset;
            var allocated = false;

            Func <Sheet> allocate = () =>
            {
                if (allocated)
                {
                    throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
                }
                allocated = true;

                return(new Sheet(SheetType.Indexed, new Size(tileset.SheetSize, tileset.SheetSize)));
            };

            random = new MersenneTwister();

            var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);

            foreach (var t in tileset.Templates)
            {
                var variants = new List <Sprite[]>();

                foreach (var i in t.Value.Images)
                {
                    ISpriteFrame[] allFrames;
                    if (onMissingImage != null)
                    {
                        try
                        {
                            allFrames = frameCache[i];
                        }
                        catch (FileNotFoundException)
                        {
                            onMissingImage(t.Key, i);
                            continue;
                        }
                    }
                    else
                    {
                        allFrames = frameCache[i];
                    }

                    var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
                    var indices    = t.Value.Frames != null ? t.Value.Frames : Exts.MakeArray(t.Value.TilesCount, j => j);

                    var start = indices.Min();
                    var end   = indices.Max();
                    if (start < 0 || end >= frameCount)
                    {
                        throw new YamlException("Template `{0}` uses frames [{1}..{2}] of {3}, but only [0..{4}] actually exist"
                                                .F(t.Key, start, end, i, frameCount - 1));
                    }

                    variants.Add(indices.Select(j =>
                    {
                        var f    = allFrames[j];
                        var tile = t.Value.Contains(j) ? t.Value[j] : null;

                        // The internal z axis is inverted from expectation (negative is closer)
                        var zOffset = tile != null ? -tile.ZOffset : 0;
                        var zRamp   = tile != null ? tile.ZRamp : 1f;
                        var offset  = new float3(f.Offset, zOffset);
                        var type    = SheetBuilder.FrameTypeToSheetType(f.Type);

                        // Defer SheetBuilder creation until we know what type of frames we are loading!
                        // TODO: Support mixed indexed and BGRA frames
                        if (sheetBuilder == null)
                        {
                            sheetBuilder = new SheetBuilder(SheetBuilder.FrameTypeToSheetType(f.Type), allocate);
                        }
                        else if (type != sheetBuilder.Type)
                        {
                            throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
                        }

                        var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
                        Util.FastCopyIntoChannel(s, f.Data);

                        if (tileset.EnableDepth)
                        {
                            var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
                            Util.FastCopyIntoChannel(ss, allFrames[j + frameCount].Data);

                            // s and ss are guaranteed to use the same sheet
                            // because of the custom terrain sheet allocation
                            s = new SpriteWithSecondaryData(s, s.Sheet, ss.Bounds, ss.Channel);
                        }

                        return(s);
                    }).ToArray());
                }

                var allSprites = variants.SelectMany(s => s);

                // Ignore the offsets baked into R8 sprites
                if (tileset.IgnoreTileSpriteOffsets)
                {
                    allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new float3(float2.Zero, s.Offset.Z), s.Channel, s.BlendMode));
                }

                if (onMissingImage != null && !variants.Any())
                {
                    continue;
                }

                templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), t.Value.Images.Length));
            }

            // 1x1px transparent tile
            missingTile = sheetBuilder.Add(new byte[sheetBuilder.Type == SheetType.BGRA ? 4 : 1], new Size(1, 1));

            Sheet.ReleaseBuffer();
        }
예제 #3
0
        public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
        {
            SheetBuilders = new Cache <SpriteFrameType, SheetBuilder>(t => new SheetBuilder(SheetBuilder.FrameTypeToSheetType(t)));

            this.fileSystem = fileSystem;
            this.loaders    = loaders;
        }