4 BPP tile generation for m74

This forum is for artists to post their music and graphics or discuss artistic matters that could be used in Uzebox games.
Post Reply
User avatar
danboid
Posts: 1939
Joined: Sun Jun 14, 2020 12:14 am

4 BPP tile generation for m74

Post by danboid »

I've started to look at mode 74 and my first question is I'm wondering how I'm going to create the graphics. As far as I'm aware, gconvert doesn't support 4 BPP yet but hopefully that should be easy enough to add, Artcfox?

Otherwise, I'd have to use one of the m74 generators included in kernel/videoMode74/generators ie c4bppsh.c

There are two problems with c4bppsh.c. The first is that it seems to expect the input image to be one (very wide) row of 8x8 pixel tiles, you can't input your tiles in rows and columns like with gconvert. The second problem is that it doesn't seem to support removing duplicated tiles like gconvert so it seems that would have to be done manually.

If I have to use c4bppsh, does anyone know of an easy way to transform an image arranged in rows and columns of tiles into one long row of tiles using the gimp, imagemagick or anything else I can run under Linux?
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: 4 BPP tile generation for m74

Post by Artcfox »

danboid wrote: Fri Feb 10, 2023 11:45 am If I have to use c4bppsh, does anyone know of an easy way to transform an image arranged in rows and columns of tiles into one long row of tiles using the gimp, imagemagick or anything else I can run under Linux?
Personally I would just use cut and paste in GIMP after increasing the width of the canvas to either 64 * 8, or 192 * 8. If you are using all 64 RAM tiles, you can't have more than 192 ROM tiles, so it shouldn't be too tedious. Mode74 uses the blitter concept, so you will get huge RAM tile gains by using partitioned sprites, so you will want to convert your 2x4 characters into the most efficient blitmaps by hand anyway, and the result of that process is one long strip of 8x8 blits.

Even when using gconvert, putting each tile first in a big strip (maybe spanning multiple rows) at the top of your image maps is very beneficial, as the game code is likely going to be looking at the tiles and doing things with them. You can pick the optimal arrangement of tiles so all collectables are first (meaning you just have to check if the tile you read is less than a certain tile number to know it is a collectable, versus having to check that it is in between two tile numbers. Similarly for hazard tiles, if you always put them at the end of your tileset, you just need to check if the tile number is greater than a certain tile number. For all of the tiles the game treats as solid, you can pack them next to each other so if a tile index is between the start and end of the solids, you know that it is solid, same for non-colliding (sky, background) tiles, one-way tiles, etc.. But my point is, pack tiles that get treated the same by the game next to each other in your tileset strip.

As long as the image that you feed to gconvert has that same tileset strip along the top of the image you give it, any image map that is built exclusively from that tileset below should have the correct indices to use with the tileset generated by the generators. Remember that gconvert generates two things, the tileset (an indexed list of pixel data representing each tile), and then a bunch of maps that are built up by referencing that tileset by index. As long as those indices seen by gconvert and the generator program match up (use the same long strip of tiles) an indexed map generated by gconvert (or even a dedicated tile mapping program) should work with a 4bpp tileset that comes from the generator program. Does that make sense?
User avatar
danboid
Posts: 1939
Joined: Sun Jun 14, 2020 12:14 am

Re: 4 BPP tile generation for m74

Post by danboid »

That is some great advice regarding the grouping of the tiles according to purpose. In IKD, all of my sprites were exactly 1 tile big and they weren't animated either so I didn't have to think about a lot of these issues, which was intentional of course.

I didn't fully understand what you were recommending tho, like:

"putting each tile first in a big strip (maybe spanning multiple rows) at the top of your image maps is very beneficial"

Could you please link to an example of what you mean by this, if you know of one. I presume you will have done this in your games?

The reason I wanted 4 BPP support added to gconvert is so that I could use the duplicate tile removal feature. My thinking here was that I should be able to place all of the frames of animation used by each character on a per character basis into one image, making sure all the frames/tiles are aligned to an 8x8 grid, and this might help creating the tileset for each character easier by having gconvert work out the duplicate tiles.

I see there is an app called Tiler that has a Linux version. I've not checked it out yet so I dunno if that might make anything easier for me vs doing it all in GIMP and a text editor?

It should be easy enough to add a 4bpp mode to gconvert right?
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: 4 BPP tile generation for m74

Post by Artcfox »

danboid wrote: Fri Feb 10, 2023 8:03 pm That is some great advice regarding the grouping of the tiles according to purpose. In IKD, all of my sprites were exactly 1 tile big and they weren't animated either so I didn't have to think about a lot of these issues, which was intentional of course.

I didn't fully understand what you were recommending tho, like:

"putting each tile first in a big strip (maybe spanning multiple rows) at the top of your image maps is very beneficial"

Could you please link to an example of what you mean by this, if you know of one. I presume you will have done this in your games?
I posted the tileset for my latest WIP here: https://uzebox.org/forums/viewtopic.php?p=33660#p33660

You can see all the lollipops tiles that trigger a pickup are grouped together, first all the ones that get animated, and next the main parts of the stick. You can also have overlapping groups. All of the tiles starting from the first lollipop up to and including the first two ladder tiles are considered sky tiles, and are ignored by the physics engine when doing collisions, but within that set of sky tiles there are subsets, like the dissolving cloud tops, and the second ladder tile you see is considered a ladder tile (the first ladder tile you see that is 1 pixel shorter is considered sky, and is only there to make it look like you are still on a ladder with your hooves, when in reality the only thing keeping you on the very bottom of an actual ladder tile is the tip of your unicorn horn). Sets of tiles can be disjoint as well. The check for ladders checks if a tile number is between two ranges of tiles. Between the two disjoint sets of ladder tiles are the one-way tiles that only have collision with the player if your previous Y position was above them and your current Y position is on or below them. Then there is another two ladder tiles, but the last one overlaps with the start of the solid tiles. It's considered both a ladder tile and a solid tile by the physics engine, and then there are the hazard tiles, and then the spawn tiles, which are only used for their index to appear in the map, the actual tile data for those gets stripped out by my Makefile (just a grep -v on any line that doesn't start with the hex codes for 16 yellow pixels in a row).

danboid wrote: Fri Feb 10, 2023 8:03 pm The reason I wanted 4 BPP support added to gconvert is so that I could use the duplicate tile removal feature. My thinking here was that I should be able to place all of the frames of animation used by each character on a per character basis into one image, making sure all the frames/tiles are aligned to an 8x8 grid, and this might help creating the tileset for each character easier by having gconvert work out the duplicate tiles.
Mode74 uses the concept of blitting, so you would be wasting a ton of RAM tiles if you define your spritemaps like that. Definitely read this multiple times until you understand the concepts: http://uzebox.org/wiki/Sprite_Techniques It talks about Mode 3, but the concepts can be applied to Mode74. By partitioning your sprites, you can make them take up fewer RAM tiles.

Definitely draw out all of your sprites on an 8x8 grid first to make it easier for you, but the de-duplication you get with a blit-based video mode is way more powerful, because you can de-duplicate 8x8 chunks that aren't even aligned to the same gridspace. Ignoring the blinking and gulping heads of my demo for a second, I only needed to define 1 unicorn head tile, drawn inside an 8x8 grid, and say that is tile number 20 in my spritemap. I can add that to many different blitmaps (animation cycles), and give it any pixel offset that I wish in any of the blitmaps, it can even be drawn 15 pixels to the left of everything else if I wish, without using any ram tiles in between. Think of each 8x8 tile in your spritemap like a stamp, and with a blit-based video mode you don't need to stamp out your tiles on an 8x8 grid, you can overlap them, make them disjoint, flip individual ones on the X and/or Y axis, and especially align them like partitioned sprites described in Lee's document, so the composition of blits making up an object takes up fewer RAM tiles overall and on average..

I described this all in great detail with screenshotted examples in my WIP thread. Here is a link to the one with the tilemap vs blitmap example: https://uzebox.org/forums/viewtopic.php?p=33683#p33683 I can stamp that head and tail at any pixel offset I wish, composing my main character from separate parts, without having to waste additional flash space because in some poses the head is 1 pixel lower within the 8x8 grid, or maybe there is a back leg that overlaps with the tail in a certain pose.
danboid wrote: Fri Feb 10, 2023 8:03 pmI see there is an app called Tiler that has a Linux version. I've not checked it out yet so I dunno if that might make anything easier for me vs doing it all in GIMP and a text editor?
Maybe, I've never used one before, but it might be a good idea if I'm going to be making a bunch of huge levels. For Bugz I just used GIMP in pixel mode, where 1 pixel = 1 ground tile, and I drew my levels with the pencil tool, and for other things like the collectables, enemies, ladders, and fire I drew them as 1 pixel (in a different color) on a different named layer. Then I ran xcf2png to extract each layer into a png file, and wrote a command line program that ran on the dev machine that processed all of the png files for each level, compressing each level down to roughly 209 bytes each, generating a .inc file for each level, which I included in the main .c file.

I'm still trying to decide how to design the levels for the unicorn game, but if you figure out how Tiled works, or Tiler, that would probably be a good thing to share on the wiki or forum.
danboid wrote: Fri Feb 10, 2023 8:03 pm It should be easy enough to add a 4bpp mode to gconvert right?
You don't need to! Once you have all of your sprite/tileset tiles that can be blitted anywhere, the only thing you need gconvert for is the generation of rectangular maps for the level right? The map is just a collection of 8-bit indices that represent offsets into your tileset. As long as the tiles you arranged in a strip for the 4bpp generator program come first and are in the same order as the tiles in the image you give to gconvert for a rectangular tile map, those indices will end up being exactly the same. If your level map as defined in gconvert has tile #5 followed by tile #2, it will store 0x05, 0x02 in the map. And as long as the tilestrips you fed to the generator program is in the same order, the maps from gconvert can be used to index into the tileset that you created using the generator program. Let gconvert create the tileset and indexed maps, but then toss out the 64 byte tilesets, and substitute the 4bpp tileset from the generator program. The maps will still have the same index values, but now they'll be pointing to 4bpp tiles.

Pretend that instead of tiles, we are talking about an indexed-color image. You can create a indexed-color image using whatever program you want, but as long as you know that color #0 comes first followed by #1, #2, #3, #4, you can associate that image with a different color palette later. The indices in the image data haven't changed, you just swapped out the palette for a different one. Think of gconvert as generating the image data, and the generator program as generating the color palette. As long as you feed the same tilestrip to both programs (gconvert needs it at the top, so the first tiles it sees are the unique ones in the tilestrip), you can use maps from gconvert with 4bpp tile definitions created with the generator program.
User avatar
danboid
Posts: 1939
Joined: Sun Jun 14, 2020 12:14 am

Re: 4 BPP tile generation for m74

Post by danboid »

Thanks for the explanation Artcfox, I'll have to read that again along with Lee's tutorial.

I must admit I'm still not 100% sure if m74 is the right choice for remaking Kung Fu master, what do you think?

So with mode 74 I'm trading the higher res and additional on screen colours offered by mode 3 for (potential) access to the SD card for tile data, a halving of space required for tile data and smoother scrolling/animation offered by m74? If you don't need 256 colours or the slightly bigger res then m74 makes more sense?

Do I not get access to more tiles when using mode 74 than mode 3? I presumed that might be the case because they consume less space but maybe the limit remains the same?

Mode3 has mega sprites or whatever it calls them. I'll have to implement my own functions for handling sprites composed of multiple tiles in m74 right?

The sprites will be screenshots from mame, shrunk down to half size and cleaned up in GIMP.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: 4 BPP tile generation for m74

Post by Artcfox »

Mode74 is complicated, but there are examples you are encouraged to start with. I have never used it before, but I have been reading about it. It definitely has a learning curve, but don't let that discourage you.

I don't think you can use the SD card for tile data unless it supports User RAM tiles, like Mode 3 does. But in Mode 3 that eats into your ram tiles budget double because they still get copied into ram tiles for blitting.

Mode74 tiles take up less flash memory so you have more flash space for other things, and they take up less RAM so you can have more of them, but the VRAM is still an 8-bit index, so you can't have more than a total of 256 divided between tiles and sprites how you choose (within the limitations). This is why developing a game for the Uzebox is so much fun! There are enough limitations that you don't need a giant team of people to create a game, while at the same time it presents a lot of creative challenges in order to overcome those limitations.

Have you been following my WIP Mode 3 Game Engine thread? I implemented my own BlitMaps exactly for the purpose of replacing MegaSprites with something way more versatile and efficient that lets you do partitioned sprites and or disjoint sprites. You could use that for storing your MegaBlits, just call the Mode74 blit routine inside your BlitMyBlitMap function instead of the Mode3 one.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: 4 BPP tile generation for m74

Post by Artcfox »

There is an advanced technique that would allow you to "increase" the tile limit beyond 192, as long as you don't use them all at once, because you are still limited by VRAM using an 8-bit index.

If you mess with the tileset pointer, you can make it appear that your game is using more than 192 tiles. If you wanted to make two complete tilesets, you could do that as long as they both fit into flash memory, and you could switch between the two by changing the tileset pointer. If you wanted to get really clever, you could use a sliding window so some tiles overlap between the two tilesets, but you'd have to be really clever about transitions to hide it from the person playing the game. Like for instance:

Say you are in a DUNGEON that uses Tileset A, and you want to transition to a PALACE that uses Tileset B, you could design your level so you always end up passing through a HALLWAY that uses a limited number of tiles that are shared between Tileset A and Tileset B.

For the sake of example, lets pretend the total tile limit per tileset was 8, and this was your PROGMEM layout (D# representing a DUNGEON tile, H# a HALLWAY tile, and P# a PALACE tile):

Code: Select all

D1, D2, D3, D4, D5, D6, H1, H2, P1, P2, P3, P4, P5, P6
When you are using Tileset 1, the DUNGEON tileset, you would set your tileset pointer to the address of D1, and your working tileset would be:

Code: Select all

{D1, D2, D3, D4, D5, D6, H1, H2}
Note that it does not include any P# PALACE tiles.

And when you are using Tileset 2, the PALACE tileset, you would set your tileset pointer to the address of H1, and your working tileset would now be:

Code: Select all

{H1, H2, P1, P2, P3, P4, P5, P6}
As you walked toward the palace though, you can (by virtue of controlling the level design) force the player to walk down a hallway that only uses tiles that are shared among both tilesets, the HALLWAY tiles, H1 and H2.

As long as you are fully in a HALLWAY, the VRAM at this point would only include indices for H1 and H2, which in the DUNGEON tileset, are indices 0x06 and 0x07.

At this point, if you just changed your tileset pointer to point to the PALACE tileset:

Code: Select all

{H1, H2, P1, P2, P3, P4, P5, P6}
everything would look wonky, because your VRAM still references indices 0x06 and 0x07, which are now PALACE tiles P5 and P6. So what you also need to do (within the same frame that you switch the tileset pointer!) is loop through VRAM and change all of the indices that reference HALLWAY tiles in the PALACE tileset to indices that reference those same HALLWAY tiles from the PALACE tileset, so 0x06 -> 0x00, and 0x07 -> 0x01. So quickly loop over all tiles in VRAM and subtract 6 from each.

And now your player can continue walking down the HALLWAY toward the PALACE, and your level can start to include PALACE tiles, because you've shifted both the tileset "window", and did a one-time remapping of the indices in VRAM, so the person playing the game was none the wiser. Even though you have a hypothetical tile limit of 8, you were able to work within those limitations to present 14 different tiles to the user. When walking from the PALACE back to the DUNGEON, you would need to do the same thing, except in reverse. If you wanted to have another area beyond the PALACE that was only accessible from the PALACE, you could do the same thing with transition tiles after P6 (assuming you had enough flash space!). Just make sure in your level design you block access to areas where you don't have transition tiles to pull off that trick.
Post Reply