Code Monkey home page Code Monkey logo

sheep's Introduction

Sheep ๐Ÿ‘

Build Status Crates.io

sheep (Spritesheet packer) is a lightweight and modular library used to create spritesheets. It aims to impose as little restrictions as possible on the usage of its API so that it can be used in asset pipelines.

The project is in heavy development and the API might change a few times until we reach a stable version, but the general flow of inputting bytes and receiving metadata and a byte vec will remain the same.

Usage

To use the CLI, simply install it with cargo:

cargo install sheep_cli

Usagen hints are provided. To see all options, simply run the command with no arguments. Options can be passed to the packers using the --options flag, as space separated key=value pairs. By default, the maxrects packer will be used, see packers for more information.

Example:

sheep pack --options max_width=1024 max_height=1024 sprites/*.png

If you want to use the CLI from source, simple clone the repo and run cargo run -- .... For an example on how to use the library directly, please see the simple_pack example in the sheep/examples directory.

Implementing your own Packer and Format

Sheep achieves its modularity by letting you choose the implementation it will use for packing the sprites and encoding the metadata. Right now, two common packing algorithms are provided (SimplePacker and MaxrectsPacker, see packers), as well as the data format used by the amethyst engine (AmethystFormat). There will be more in the future, however, you can also choose your own packing algorithm and format:

Implementing Packer

pub struct MyPacker;

impl Packer for MyPacker {
    fn pack(sprites: &[SpriteData]) -> PackerResult {
        // Spritedata contains an id for the sprite to reference back
        // to it, and the dimensions of the sprite.

        // The expected output is the dimensions of the resulting spritesheet,
        // as well as all the anchors for the sprites (i.e. their positions).
        PackerResult { dimensions, anchors }
    }
}

Implementing Format

pub struct MyFormat;

// This is the format that will be output by encode, and you'll probably want
// to serialize later.
#[derive(Serialize)]
pub struct Foo {}

impl Format for AmethystFOrmat {
    type Data = Foo;

    fn encode(dimensions: (u32, u32), sprites: &[SpriteAnchor]) -> Self::Data {
        // Encode the spritesheet dimensions and sprite positions into
        // your chosen data format here.

        Foo {}
    }
}

Using your custom impls

To use custom packers or formatters, simply pass them as type parameters when calling the functions:

let sprite_sheet = sheep::pack::<MyPacker>(sprites, 4);
let meta = sheep::encode::<MyFormat>(&sprite_sheet);

Packers

Right now, there are two implementations to choose from:

  • MAXRECTS (recommended)

Implementation of the maxrects sprite packing algorithm. The paper and original implementation used as a reference for this can be found here. This algorithm should yield optimal results in most scenarios.

  • simple

A naive implementation that will sort the sprites by area and then pack them all into a single texture. This won't scale very well since you can't limit the maximum size of the resulting sprite sheet, but can be quicker than maxrects in simple scenarios.

Roadmap

Here are the planned features for sheep:

  • Support for multiple output textures (bins)
  • Smart output texture sizing
  • More packing algorithms
    • MAXRECTS
    • Skyline
  • More meta formats
  • More image formats

License

sheep is dual licensed under MIT and Apache, see COPYING.

sheep's People

Contributors

aldaronlau avatar alec-deason avatar awpteamoose avatar barskern avatar basil-cow avatar bors[bot] avatar covercash2 avatar happenslol avatar qthree avatar torkleyy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sheep's Issues

Improve named meta format

sorry, i'm the author of the named packer, but it's not working. because the packer changes the order of the sprites and doesn't retain information about how that order is changed, i had to go in and add fields to a lot of intermediate data types, which really messes with the original architecture but works for my purposes. i basically attached the file name to every intermediate data type. you can look at my quick and dirty changes here.

i didn't want to open a pull request yet, since, like i said, it's pretty dirty and pokes some holes in the original architecture. i just wanted to get some thoughts on how to change this approach so it could work.

Rounding to power-of-two dimensions

First, thanks for publishing sheep! It was heartening to see other people working on asset pipeline tooling for games in Rust.

In a tool my team is working on, we need to pack images into square power-of-two textures for some devices, preferring to waste a little space to get a square image instead of a perfectly packed non-square image.

When using Sheep's Max Rects packer with five 128x128 textures and a max size of 1024x1024, it produces a 128x640 texture like this:

22d80dd5ebdb967194b2d144f57eee3e

We'd prefer that it instead produce a 512x512 square texture with wasted space. On some platforms, would have to expand a 128x640 texture into a 1024x1024 texture, which is more wasteful!

We've found some other tools that mention the problem of PO2 textures with maxrects, but I'm not familiar enough with packing algorithms to know if it's feasible to enforce exactly.

Named format uses `Vec<String>` instead of `&[String]`

Currently, AmethystNamedFormat takes extra options in the form Vec<String>. This is suboptimal since it requires moving the whole container and cloning it if it needs to be reused. Instead it could be &[String], or even &[S] with AmethystNamedFormat<S> where S: Deref<Target=str>.

I'm happy to write up a PR for this if people think it's a good idea.

O(n^2) complexity in sprite/anchor matching

When finding an anchor corresponding to a sprite in pack(), program iterates over all anchors for each sprite. I assume that it's for maintaining sprite order (but they could be still in different sheets, right? so why does it matter if they are sorted?). O(n^2) can be reduced to O(n) with a hashmap of anchors, but maybe I'm missing some context why it was done the way it is.

Sprite order

Atm arguments order doesn't match the output sprites order, so changing assets or adding new ones breaks all sprite ids.

Hashing the output

I think it's an important feature is to keep a hash of the input data and options somewhere in the output folder in order to do nothing in case nothing changed since the last time the packer ran so you can call sheep painlessly on your game startup.

Sheep CLI options parsed as files

The README says to run the CLI like this:

sheep pack --options max_width=1024 max_height=1024 sprites/*.png

I tried it and it did not work. Adding the options after works:

sheep pack sprites/*.png --options max_width=1024 max_height=1024

I think the options might get interpreted as files?

Bug: sheep-cli panics on input with globbing (Windows)

Issue

When I specify the input files using globbing, sheep-cli crashes with

thread 'main' panicked at ' Failed to open image: IOError(Os { code: 123, kind: Other, message: "The filename, directory name, or volume label syntax is incorrect." })

I looked into it and found out that this is due to Windows not expanding input arguments using globbing.

Possible Fix

Use glob crate to expand inputs with globbing.

Implement image output compression

As mentioned in #12, it would be great to have some sort of image compression/optimization for the output, comparable to pngquant. For rust, interesting options for this would include oxipng and mtpng.

We should think about whether or not it makes sense to add this functionality to the library itself, since this is probably something that - when people integrate the library into their own asset pipeline - would be replaced by existing asset processing infrastructure.
Having this option available in the CLI definitely sounds like a good idea to me, though.

Feature requests (from texturepacker)

https://www.codeandweb.com/texturepacker
has a plethora of features that'd be useful

  • pngquant compressor export option
  • trim transparent borders
  • detecting identical sprites and aliasing them in the ron file

texturepacker also has a custom exporter to template output files, so someone could try to make one for a .ron file. Only have 30 days to use a good amount of the features though so glad sheep is in the making.

Amethyst format is broken for 0.15

0.15 now requires a List(()) to surround the ron file. Example: https://github.com/amethyst/amethyst-starter-2d/pull/14/files#diff-ee91fa974052bd6353c9077e79d522eb

From the changelog for 0.14:

Changed SpriteSheetFormat::import_simple to allow importing grid based SpriteSheets (#2023)
Migration Note: Rons need to wrap their content in either Grid() or List()

Error:

[ERROR][amethyst_assets::storage] "renderer::SpriteSheet": Asset "textures/spritesheet.ron" (handle id: Handle { id: 0 }) could not be loaded: Failed to load asset with name "textures/spritesheet.ron"
[ERROR][amethyst_assets::progress] Error loading handle 0, renderer::SpriteSheet, with name textures/spritesheet.ron: Failed to load asset with name "textures/spritesheet.ron"
caused by: Failed to load asset with name "textures/spritesheet.ron"
Error { inner: Inner { source: Some(Error { inner: Inner { source: Some(Error { inner: Inner { source: None, backtrace: None, error: LoadSpritesheetError(Parser(ExpectedIdentifier, Position { col: 1, line: 1 })) } }), backtrace: None, error: Format("SPRITE_SHEET") } }), backtrace: None, error: Asset("textures/spritesheet.ron") } }
caused by: Format "SPRITE_SHEET" could not load asset
Error { inner: Inner { source: Some(Error { inner: Inner { source: None, backtrace: None, error: LoadSpritesheetError(Parser(ExpectedIdentifier, Position { col: 1, line: 1 })) } }), backtrace: None, error: Format("SPRITE_SHEET") } }
caused by: Failed to parse SpriteSheet
Error { inner: Inner { source: None, backtrace: None, error: LoadSpritesheetError(Parser(ExpectedIdentifier, Position { col: 1, line: 1 })) } }
$ sheep --version
sheep 0.3.0

Textures overlap

My texture atlas looks like this in some cases
Screenshot from 2019-10-16 17-53-24
Probably it's due to I'm using too big textures(I will write here when I resize them).

Skyline packer

I've read about skyline heuristics given in 2011 and 2016 works and they both work by trying to achieve as low height as possible given a width of a spritesheet.

  1. I'm not sure how to adopt this to output multiple spritesheets, since their approach relies heavily on being able to shuffle the input. One possible approach is trying to fit it into one spritesheet, then if it fails, splitting the input randomly into two parts and trying to fit it into two spritesheets and so on. I'm not sure this is an optimal approach and would like to hear your thoughts.

  2. I'm not sure how to choose the width of a spritesheet, I don't really like maxrects approach of giving it a default value since in it will just produce a long horizontal stripe of sprites and that doesn't feel optimal (maybe it's really efficient or doesn't make a difference at all, I don't know). So I propose to make some kind of algorithm on top of skyline (and maybe maxrects too) that tries to determine optimal width and height. Again, I'd like to hear your thoughts.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.