Page 2 of 2

Re: New efficient metasprite format

Posted: Tue Feb 26, 2013 6:23 am
by tokumaru
Sik wrote:Flipping is not as trivial since you also need to modify the coordinates of the sprites. It's a good starting point for that goal, though.
Yes, but in my case at least, besides being used to flip the bits in the attribute bytes, the modifier byte is checked at the beginning of the metasprite function and used to adjust the sprite coordinates and increments.

EDIT: I also should point out that EORing palette bits wouldn't work so well if you use more than one palette in the same metasprite... for example, say that an enemy is green and wears a white shirt, so it uses different palettes for the green and white parts. If you want to recolor that enemy so that it's blue but still wears a white shirt, that wouldn't work, since the palette of the shirt would be changed as well. One solution would be to use OR or AND instead of XOR, so that you can use palette 0 (%00) or 3 (%11) for the one you don't want to change. That would require more careful planning of the palettes.

Re: New efficient metasprite format

Posted: Tue Feb 26, 2013 1:55 pm
by Bregalad
I think I got a better idea, that optimises the usage of all 4 unused bits (counting the priority bit as unused).
I forgot the idea to predict the attribute byte, as it would be very easy to screw up and end in an infinite loop.

Just remember, as I said in my 1st post :
- Attribute byte is always present
- X, Y and tile # bytes are either predicted from the previous sprite, or present
- Prediction data for sprite N+1 is stored in the data of sprite N
- The first sprite is never compressed (always 4 bytes)

Code: Select all

Code:
yyyyyyyy --- Sprite Y coordinate (if required)

vhtyxxpp --- Attribute byte (always)
||||||||
||||||++---- Sprite Colour
||||++------ X position of next sprite (00 : required, 01 : X pos of this sprite + 8,
||||                                    10 : X pos of the fist sprite, 11 : Escape code for last sprite)
|||+-------- Y position of next sprite (0 : required, 1 : Y pos of this sprite + 8)
||+--------- Tile number of next sprite (0: required, 1 : Tile number of this sprite + 1)
|+---------- Horizontal flipping
+----------- Vertical flipping

tttttttt --- Tile number (if required)

xxxxxxxx --- Sprite X coordinate (if required)
The enthropy of the 4 unused bits are not fully used, as 4 of the 16 possible shemes have the same effect (finishing the meta sprite), but there is still 12 predictions available.
As opposed to my first idea for the codec, it makes it possible for tiles which are gird aligned in a direction but not the other to be still compressed efficiently. Those are pretty common, every time an enemy has a tail I use this, so the tail is in the middle of the sprite but still takes only 1 tile of width/height, so I move it 4 pixels apart the "gird".

Again, this is fully compatible with uncompressed sprites, except that you have to OR the attributes of the last sprite with $0c for it to work.
I also should point out that EORing palette bits wouldn't work so well if you use more than one palette in the same metasprite... for example, say that an enemy is green and wears a white shirt, so it uses different palettes for the green and white parts. If you want to recolor that enemy so that it's blue but still wears a white shirt, that wouldn't work, since the palette of the shirt would be changed as well. One solution would be to use OR or AND instead of XOR, so that you can use palette 0 (%00) or 3 (%11) for the one you don't want to change. That would require more careful planning of the palettes.
Mmh, you got a point !
The problem with the AND/OR approach is that it's then impossible to access all 4 palettes by the means of palette swapping unless you use palette 3/0 (respectively). However it will fix the problem you issused, which is a good thing.
Flipping is not as trivial since you also need to modify the coordinates of the sprites. It's a good starting point for that goal, though.
Yes, usually you also need to negate (XOR with $ff) the X coordinate.
You could also do that vertically, but unless you have a level like Gravity Man's, I doubt it will be very useful.

Re: New efficient metasprite format

Posted: Tue Feb 26, 2013 3:14 pm
by Drag
Bregalad wrote:
Flipping is not as trivial since you also need to modify the coordinates of the sprites. It's a good starting point for that goal, though.
Yes, usually you also need to negate (XOR with $ff) the X coordinate.
XOR $FF and then add 1 to it. (two's complement)

You can easily add the 1 to it by setting carry before you ADC the metasprite's x coordinate to the tile's x coordinate.

Re: New efficient metasprite format

Posted: Tue Feb 26, 2013 5:08 pm
by Tom
tokumaru wrote:Reusing parts of other metasprites is trickier... Maybe you could have a special code that acts like a JSR of sorts for metasprites, so you can "call" parts that are shared between metasprites. Or maybe you can allow animation frames to have more than 1 metasprite, so you can combine smaller metasprites as necessary. I don't think this is worth it though.
I do something like this, and the savings I get are pretty decent, with the compressed size being about 70% of uncompressed size. Also I find specifying the metasprites is a lot easier being able to reuse subparts. This probably depends on the game though.

That said, I'm beginning to question if metasprite compression is really worth it. They don't take up a lot of space compared to other things, and a significant amount of time per frame is spent drawing metasprites. Having them uncompressed would speed that up, and I could probably get a lot more objects on screen before lagging.

Re: New efficient metasprite format

Posted: Tue Feb 26, 2013 5:19 pm
by 3gengames
To really flip sprites, you need to do a lot more than modify the X coord by 2's complement. You need to add (tilewidth-1)*8 to it, and then store a variable in RAM that's either 8 or -8, and then just use a CLC+ADC with it each time you move either a X or Y coord. Not that hard, but still, it's something to know before hand.

(I'm writing a metasprite engine at this very moment....or at least am deep in to planning and am about to start writing the code very soon, so whatever I do I'll probably input here for ideas on how to make it better.)

Re: New efficient metasprite format

Posted: Thu Feb 28, 2013 12:27 am
by sdwave
@Bregalad: Just curious, how many metasprite frames do you have and how much data is there? Are you finding trading time for space savings to be a win? It almost seems to be worth encoding the mirrored versions of the metasprites rather than doing the mirroring in code?

What I ended up doing was having a separate page for static data like metasprites. Each sprite in the metasprite takes up 4 bytes that match the OAM layout (delta y, palette mask, index, delta x) in order to trade size for speed.
Mirrors are encoded instead of computed. Each frame of body animation has socket locations for arms and head so the character can aim in any directions for any frame of animation. I need to take a second pass over the main character to get a smoother run sequence so I'm revisiting my scheme. I think I'll need to make a tool for metasprite layout (so far I've been doing it by hand.)

Re: New efficient metasprite format

Posted: Fri Mar 01, 2013 10:55 am
by Celius
3gengames wrote:To really flip sprites, you need to do a lot more than modify the X coord by 2's complement. You need to add (tilewidth-1)*8 to it, and then store a variable in RAM that's either 8 or -8, and then just use a CLC+ADC with it each time you move either a X or Y coord. Not that hard, but still, it's something to know before hand.

(I'm writing a metasprite engine at this very moment....or at least am deep in to planning and am about to start writing the code very soon, so whatever I do I'll probably input here for ideas on how to make it better.)
I actually made a separate sprite drawing routine that does SBC instead of ADC for flipping entire metasprites. It seems like a tacky solution, but it actually saves a lot of cycles in the end. This way you don't have to include another variable into the calculation of every sprite position in a metasprite.