Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to import and create palette indexed sprites from GIFs and APNGs #475

Open
Two-Tone opened this issue Feb 14, 2020 · 15 comments

Comments

@Two-Tone
Copy link

Ported from godotengine/godot#18269

Describe the project you are working on:

Unrelated

Describe the problem or limitation you are having in your project:

Currently if a developer is going to use 2D animated sprites, they have to go through a semi complicated process for creating and animating it in Godot. The more animations you need to add to your project, the more tedious and prone to error it becomes.

Similarly, if the dev wants to display a sprite with its palette swapped (eg it's a unique version of an enemy so it's yellow instead of purple) they have to either use a shader to shift its entire palette, recolor the sprite in their image editor of choice, or develop a semi-complex shader that dynamically swap palettes. The first option, while fast and easy, gives almost no precise control, the second ends up creating unnecessary asset bloat, and both the last two options are labor intensive and slow.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:

Some engines, like MUGEN, have the ability to import GIFs and automatically convert them into animated sprites. This makes creating and adding 2D animations into the engine incredibly easy and fast.

MUGEN also expands on this by allowing the user to edit the indexed palette of sprites to very quickly create palette swapped sprites.
pal

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:

The biggest issue I see is figuring out the process the user needs to go through to have a GIF be turned into a fully animated Sprite in engine. This can't be done on import because where would the editor put the new nodes? It could save each animation as a new scene with the AnimationPlayer as the root node, but that would be pretty unique behavior to GIFs and APNGs.

The easiest solution I can think of is having the user drag the GIF/APNG into there scene and then create the tree of nodes.

For the palette I can see it being one of two systems. The most user friendly one would be to have a palette resource in the StreamTexture generated on Import that when clicked into they see each paletted color as a clickable square and then they can start editing the palette as they desire, similar to the GIF above.

The second system would be easier but it wouldn't be user friendly and it'd be considerably slower and more cumbersome. The positive is that it wouldn't require much work to make vs the system above. When inside the StreamTexture the user could edit the palette by selecting its number on the index slider and changing it below that.
image

If this enhancement will not be used often, can it be worked around with a few lines of script?:

No

Is there a reason why this should be core and not an add-on in the asset library?:

It'd massively improve the workflow for adding in animations and creating palette swaps.

@KoBeWi
Copy link
Member

KoBeWi commented Feb 14, 2020

The biggest issue I see is figuring out the process the user needs to go through to have a GIF be turned into a fully animated Sprite in engine.

It could be imported as AnimatedTexture.

@Two-Tone
Copy link
Author

TBH, I forgot that AnimatedTextures exist. I've never used any in any of my projects yet and setting them up right now looks to be a huge pain.

But yes, if it were set up automatically by importing a GIF/APNG then that would probably be best.

@Janglee123
Copy link

Adding GIF support seems nice but it has some drawbacks.
Perhaps, AnimatedSprite is more suitable in this case.

@Two-Tone
Copy link
Author

Two-Tone commented Feb 15, 2020 via email

@Janglee123
Copy link

I am not clear what you are trying to say. Do you want one animation per GIF or all animation in ONE GIF? The first one can be implemented with AnimationSprite. The second one is overkill.

@Xrayez
Copy link
Contributor

Xrayez commented Feb 17, 2020

I pray for GIF support in godotengine/godot#31831. 🙂

@jjhaggar
Copy link

I'm glad this proposal is alive ^_^

If Godot gets the ability to use indexed palettes natively (using gifs, or indexed pngs, or act palette files, or any other straightforward method), I think it would instantly become the best engine for modern pixel art games.

I really look forward to it! :D

@Xrayez
Copy link
Contributor

Xrayez commented Sep 26, 2020

See also #1433 and a more underlying #1448 proposals to help this.

If Godot gets the ability to use indexed palettes natively

This is unlikely to be implemented in core, because indexed image support was removed by the lead developer some time ago (in Godot 3.0), under the justification of that modern GPUs don't support indexed images.

I'm developing a module which acts as an extension for Godot which does provide ImageIndexed classes, and even recently ported the GIF loading and import support, see goostengine/goost#8 (big credits to the original author who made this in godotengine/godot#31831). Of course there are a lot more which can be done on the editor side, this only provides core functionality, and I'm not using those features for pixel-art use cases myself to be honest.

The idea isn't to support gifs and apngs as formats in engine, but to support them in the importer to improve the workflow of 2D developers.

If you ask for import features, then this also implies loading support of those image formats as well. On the contrary, the loading part can be implemented alone in the engine, so that you're free to use those animated image loaders to implement your own importers of your liking (AnimatedTexture, SpriteFrames, AnimatedSprite, AnimationPlayer etc.) via editor plugins, which should be quite possible to do via script.

This is why I went for alternative proposal in #1448, so the engine can facilitate those use cases on the core level, but not necessarily implement them.

@Xrayez
Copy link
Contributor

Xrayez commented Jun 3, 2021

According to decision done by reduz in godotengine/godot#31831 (comment), unfortunately, I don't think that this proposal has a chance to be implemented in core. There might be a chance that GIF support could be merged for the use case of displaying animated images in asset library (and it would likely be editor-only), but that's probably all.

But indexed images and ability to import GIFs as AnimatedTextures are features which are part of Goost project now, of which I'm a creator and maintainer. There are no editor tools implemented to edit and manipulate palettes, though. To be honest, I didn't need those features for the functionality as described in this proposal (which are a bit niche considering the fact that you can achieve the same thing via shaders nowadays).

@seocwen
Copy link

seocwen commented Nov 22, 2021

As far as proposals go, importing animated images and supporting indexed color are separate issues. Sprite sheets work best for most purposes as it's very common to reuse animation frames between cycles. Baking them into separate resources can lead to maintenance problems when you have to fix the same frame in multiple places. (Eg, a squat frame appearing in animations for ducking, jumping, landing, or an uppercut appearing in multiple GIFs). I don't doubt there are people who would prefer using GIF's to animate, but it probably makes sense to import them as if they were sprite-sheets just like Aseprite converts its animations to when saving out.

Indexed color, meanwhile, is very common in pixel art--even the norm. Any workflow that prevents artists and designers from enforcing palettes is just dreadful, and these are the last people Godot should be forcing to struggle with shader code. Indexed color is incredibly powerful for designers. Besides multiplying the number of texture resources available against palettes, it allows a lot of fine-grained control over the look and feel of the art. It's not as simple as just rotating hues around or doing a color multiply, which often looks terrible--it takes a deep understanding of color ramps to design a good palette, something a shader just can't do. Go search "Godot Palette Swap" and you'll see how important this feature is to artists and designers you're not likely to find on Github.

Furthermore, while there are a number of shaders attempting to addressing this problem, there are many reasons why they fall short of the needs of artists:

  • these shaders can be difficult for artists and designers to follow
  • It's difficult to pass the palettes themselves because it's unclear which resource one should use--individual colors, 1-d textures, etc.
  • Using individual colors is safer but inflexibly links the shader to a particular number of colors.
  • Working with 1-d textures can cause problems due to the complexity of sampling, filtering, and floating point errors in reading both the indexes and palette entries. These are difficult problems for an artist to navigate.
  • Formatting artwork for these shaders inhibits smooth workflow. These palettes are not human friendly to work with. If you just use the red channel as indexes, you're left with a black and crimson-black splotch. You have to swap your palette out to a shader friendly one every time you save your artwork.
  • Trying to make a shader that can handle human friendly palettes is not tractable. You don't want to be looking for best fit-matches in a 3D color space over an entire palette in a shader. Even if you could guarantee an exact color smoothly translating into an index (again, the issues with sampling, filtering, and floating point math are far from obvious) it is not a general purpose solution. You will need a new shader for every palette size and palette ramp design.
  • In 3.4, each object that needs to pass its own data needs its own shader instance. This means that if you have multiple palette swaps onscreen, you're running a shader for each one. I've gotten slowdown working with 8-bit style graphics. My understanding is 4.0 does this better, but I'll wait and see.
  • There's no support for palette swapping on individual tiles in tilemaps. If anyone can explain to me even a workaround, I'd be grateful, because the artist in me groans at slapping a "modulate" on a texture and calling it a day. I understand that in 4.0 tiles can have individual shaders applied to them, but the workflow of passing palette data to them is unclear.

As I see it, the best solution is to create an IndexedColorTexture for imported indexed color PNG's (Which Godot should do regardless, even if it converts them to RGB). Maybe we could allow for 2bpp, 4bpp, 8bpp, or other formats I've seen, but I think PNG is the main one. We would then create a ColorPaletteTexture for importing ACT files, or any of the other common palette formats. IndexedColorTexture would export their ColorPaletteResource to the editor so that the user can swap them in and out. This gives responsibility for palette design back to the artist where it belongs, and not with a graphics programmer.

The core developers are amazing and talented human beings. I find it difficult to believe that they would not be able to write dependable, optimized code in Vulkan for treating indexed color textures as single channel textures and using their values to do a lookup into a palette. If that were a problem for them, why would they depend on lowly artists and designers to manage in the Godot Shader Language instead? I can only assume they are taking their cue from the GPU designers, who tend to overlook the needs and desires of 2D artists, not appreciating how important, useful, or necessary these features are because coders and designers are not often able to adequately communicate their needs. Certainly for Godot, whose users a predominantly working in 2D, the need is there.

@KoBeWi
Copy link
Member

KoBeWi commented Nov 22, 2021

btw about shaders, I recently created yet another palette swap shader for the asset lib (not yet accepted): https://github.com/KoBeWi/Godot-Palette-Swap-Shader
You just put image and then put palette. It can't be any simpler (I mean for a shader). It breaks with filtering, but it's not a concern for pixel-art.

@seocwen
Copy link

seocwen commented Nov 22, 2021

btw about shaders, I recently created yet another palette swap shader for the asset lib (not yet accepted): https://github.com/KoBeWi/Godot-Palette-Swap-Shader
You just put image and then put palette. It can't be any simpler (I mean for a shader). It breaks with filtering, but it's not a concern for pixel-art.

Cool stuff. Any idea how you'd use this to tile-swap individual tiles in a tilemap?

@KoBeWi
Copy link
Member

KoBeWi commented Nov 22, 2021

Not sure about 3.4, but in 4.0 you can do many things with the tiles. Like, there is this "custom data" that you can define per tile. You can e.g. create a data table called "material group", then assign each tile an integer value. Then you need to add a script to TileMap that would assign material based on your material group integer. This way you can assign one material to multiple tiles very easily and then edit it. It's also relatively efficient, because the material is shared between tiles in a group. You can export the used materials to the inspector or even just export texture fields and assign them to material when modified.

Of course this is not something that can be done by an artist, but the things that could be done without programming are limited anyways. Most of the time you need to make a script that makes doing stuff easier.

@Akaza19

This comment was marked as off-topic.

@Calinou
Copy link
Member

Calinou commented Aug 2, 2022

@Akaza19 The godot-proposals repository is not the place to ask support questions (especially for non-Godot engines).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants