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

Sprite definition format #965

Closed
TheJJ opened this issue Jan 25, 2018 · 9 comments · Fixed by #1317
Closed

Sprite definition format #965

TheJJ opened this issue Jan 25, 2018 · 9 comments · Fixed by #1317
Labels
area: assets Involved with assets (images, sounds, ...) area: renderer Concerns our graphics renderer just do it You can start working on this, there should be nothing left to discuss nice new thing ☺ A new feature that was not there before

Comments

@TheJJ
Copy link
Member

TheJJ commented Jan 25, 2018

We have to get rid of the crappy csv files that describe where subtextures are. Instead, we need to create far more sophisticated™ key-value files that describe animations, frames and angles.

Why not nyan? The frame and animation definitions are directly linked to the image files. They don't change at runtime. And the renderer can directly organize the sprites internally without nyan queries.

@TheJJ TheJJ added area: renderer Concerns our graphics renderer nice new thing ☺ A new feature that was not there before area: assets Involved with assets (images, sounds, ...) just do it You can start working on this, there should be nothing left to discuss labels Jan 25, 2018
@TheJJ
Copy link
Member Author

TheJJ commented Jan 25, 2018

@phrohdoh
Copy link
Contributor

phrohdoh commented Feb 8, 2018

Have you considered an existing textual format such as toml?

@zuntrax
Copy link
Contributor

zuntrax commented Feb 11, 2018

We had a discussion in IRC, a C++ toml does not seem to be packaged on all the common distros. We generally would like to avoid pulling another dependency for that. Parsing whatever format we choose in python should be easy and allows using existing formats like json if we want.
Nyan has enough fancyness, I'd like to use something that people are used to already.

@TheJJ TheJJ added this to todo in renderer May 2, 2018
@TheJJ
Copy link
Member Author

TheJJ commented Feb 26, 2019

propsed specification:

Sprite File Formats

.sprite files define a unit/building/... sprite and its animations.

.terrain files define a terrain sprite and its properties like blending and animation.

Both file types contains key-value pairs.
They select properties like which .png has which frames, where the frames are and where the hotspots are, what animations are provided for which direction, and what the animation mode/speed is.

Why not nyan?

https://github.com/SFTtech/nyan has quite some overhead and is not as compact as this format.
The sprite files can directly be loaded into the renderer and bypass nyan.
The animations are requested by the game simulation, the renderer displays them independently by, for example, choosing the best matching angle.

Requirements

.sprite file

  • Source image definitions
  • Layers for the individual parts of the animation
  • For each layer: mode (loop, once, randomize_loop_fps?, directional?), position (number or standard profile for shadows/buildings/garrison flags), time_per_frame (speed) and replay_delay
    • mode=loop: run with fixed framerate in loop
    • mode=once: run with fixed framerate and don't loop
  • For n angles, define the frames
  • For each frame: layer_id, image_id, xpos, ypos, xsize, ysize, xhotspot, yhotspot

What the .sprite can define:

  • One unit animation for all possible directions.
  • Multi-sprite views like the mill, blacksmith, gate (with layers)

What the .sprite can NOT define (multiple .sprites are defined in nyan):

  • Snow/alternative graphics
  • Damage graphics
  • Cultures (different looking buildings)
  • Unit modes (standing, walking, gathering, ...)
  • Building annexes like the town center

.terrain file

  • Reference source image
  • Declare pixels per tile
  • Define blending properties
  • Layers
  • Terrain tile size description to scale the terrain texture correctly

Format

.sprite file

# comments are ignored
version 0             # file version, so we can maybe extend the format

# image file reference, relative to this file (-> .sprites can reference other mods' png files)
imagefile <image_id> <filename>

# layer definitions, from bottom to top (later defined layers overdraw earlier layers)
# all layers will be drawn.
layer <id> mode=off  position=<default|roof|shadow>
layer <id> mode=once position=<default|roof|shadow> time_per_frame=<float>
layer <id> mode=loop position=<default|roof|shadow> time_per_frame=<float> replay_delay=<float>

# define an angle where frames can be assigned to or mirror from an existing angle
angle <degree> mirror-from=<existing_angle>

# assign frames to their layers and angles.
# angle is the direction in degrees, 90 = east, etc.
# *pos, *size and *hotspot is within the source image.
# all the hotspots of the frames will be drawn at the same pixel (requested by renderer)
# so that the alignment/movement of the frames is done solely by hotspots.
frame <layer_id> <angle> <image_id> <xpos> <ypos> <xsize> <ysize> <xhotspot> <yhotspot>

.terrain file

# comments are ignored
version 0

# priority for selecting this terrain's blending mask
blending_priority <int>

# selection of blendomatic borders
blending_mask <blend_id> <filename>

# how many dots in one frame are used for one tile
dots_per_tile <float>

# source image definitions
imagefile <image_id> <filename>

# layer and animation definitions
# layers defined first will be overdrawn by later definitions
layer <id> mode=off
layer <id> mode=loop time_per_frame=<float> replay_delay=<float>

# definition of a terrain frames
# these are iterated for an animation
frame <layer_id> <image_id> <blend_id> <xpos> <ypos> <xsize> <ysize>

Examples

villager_female_walking.sprite

One png file has the whole sprite sheet and we just pick the right places for all the walking directions.
Walking to the left uses the same sprites as walking to the right, just mirrored.
The shadow is already included in the same png.

version 0
imagefile 0 female_walking.png
layer 0 mode=loop position=default time_per_frame=0.1 replay_delay=0.0
angle 90  # 90-degree = walk right

frame 0 0 3 5 40 60 20 20
frame 0 0 33 35 70 90 50 50
...

angle 270 mirror_from=90
angle 180
frame 0 0 .....

Random house graphics

House with random graphic, needs multiple .sprites.
Each sprite file defines one graphical variant out of the "houses" spritesheet texture.

variant0.sprite

version 0
imagefile 0 houses.png
imagefile 1 house_shadows.png
layer 0 mode=off position=default
layer 1 mode=off position=shadow

angle 0
frame 0 0 0 0 50 50 25 25
frame 1 1 0 0 30 50 TODOOOOOOOOO

variant1.sprite

TODOOOOOOOOOOOOO
version 0
imagefile 0 houses.png
layer 0 mode=off
frame 0 0 50 0 50 50 75 25

Wall gate:

4 .sprites:

  • gate-open.sprite
  • gate-closed.sprite
  • gate-opening.sprite
  • gate-closing.sprite

In each, the animations and frames for each angle are stored.
Handling must be done in the engine, their overlay etc is done with nyan.

@brisvag
Copy link
Contributor

brisvag commented Nov 21, 2019

As I mentioned here, there are some things I noticed while working on the sprite formatter:

  • In the explanation, mirror_from is on a different line than angle definitions, but in the example, it's on the same line. I would suggest leaving it on the same line.
  • values are sometimes represented with keywords (in the form mirror_from=...) and sometimes pure data (such as frame 0 0 3...). Is there a reason for not using the same system in all places? keywords seem better for readability and modding, while plain data is better for size (though it's probably not an issue), and likely parsing; having two arbitrary methods seems a bit strange.
  • related to the previous point: in the example some lines have variable number of members (layer's position parameter can be omitted). Does this mean the data is actually missing, or simply that you would omit it when the value is equal to a "default"?
  • in the sprite definition, angle information is carried by each frame. It seems redundant to have it there and also divide them under angle X categories. I would suggest using only categories (so mirror_from is easier to see/implement) and leaving out the angle from the frame data.

@heinezen
Copy link
Member

In the explanation, mirror_from is on a different line than angle definitions, but in the example, it's on the same line. I would suggest leaving it on the same line.

That was an error in the definition. It is supposed to be on the same line. Fixed it in jj's post :)

values are sometimes represented with keywords (in the form mirror_from=...) and sometimes pure data (such as frame 0 0 3...). Is there a reason for not using the same system in all places?

It is very pythonic (like calling a method). Mandatory values have fixed positions and require no keyword, all other values have a keyword because they are only valid for specific modes or optional. Plain data will also be marginally faster to parse.

related to the previous point: in the example some lines have variable number of members (layer's position parameter can be omitted). Does this mean the data is actually missing, or simply that you would omit it when the value is equal to a "default"?

There is no default value in that case. Sometimes the value is simply not be necessary. replay_delay makes no sense for animations that do not loop for example.

in the sprite definition, angle information is carried by each frame. It seems redundant to have it there and also divide them under angle X categories. I would suggest using only categories (so mirror_from is easier to see/implement) and leaving out the angle from the frame data.

Hmm.. what would be the benefit of that? Internally, the frame must be attached to an angle anyway and we would have to remember the previously defined angle. We can do it of course, but I do not see a real benefit.

@brisvag
Copy link
Contributor

brisvag commented Nov 23, 2019

There is no default value in that case. Sometimes the value is simply not be necessary. replay_delay makes no sense for animations that do not loop for example.

Gotcha.

Hmm.. what would be the benefit of that? Internally, the frame must be attached to an angle anyway and we would have to remember the previously defined angle. We can do it of course, but I do not see a real benefit.

It just felt unnecessary to have the redundancy, it's more stuff to parse. So this makes me think: does angle information (stuff like mirror_from) come from a different place altogether than frame information?

@heinezen
Copy link
Member

Let's keep the redundancy for now as it might make debugging a tiny bit easier. We can always change it later on when everything works well.

does angle information (stuff like mirror_from) come from a different place altogether than frame information?

Yes, some attributes of a sprite come from the .dat file, some from the SLP/SMP graphics file. Frame attributes are from different places and graphics for example.

@TheJJ
Copy link
Member Author

TheJJ commented Dec 4, 2019

It just felt unnecessary to have the redundancy, it's more stuff to parse. So this makes me think: does angle information (stuff like mirror_from) come from a different place altogether than frame information?

It's true, we could implicitly assign an angle by assigning it to the last-defined angle. I made that explicit to allow rearranging the lines: you could with the explicit angle reference first define all angles, and then assign all sprites to their angles.

@SFTtech SFTtech deleted a comment from SFTbot Feb 10, 2020
@heinezen heinezen linked a pull request Sep 9, 2020 that will close this issue
convert automation moved this from output to done Sep 23, 2020
renderer automation moved this from todo to done Sep 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: assets Involved with assets (images, sounds, ...) area: renderer Concerns our graphics renderer just do it You can start working on this, there should be nothing left to discuss nice new thing ☺ A new feature that was not there before
Projects
No open projects
convert
  
done
renderer
  
done
Development

Successfully merging a pull request may close this issue.

5 participants