Find the Story (in development for FX)

firmware.hex (61.2 KB)

I got a little time last weekend to get some of my proof-of-concept code verified, so I’m ready to start talking about my game jam entry. This is still very much Work-in-Progress, but I’ll move it over to the Jam category once I’ve got the actual game play loop implemented.

My idea for Find the Story is one I’ve had for a while. I’ve loved the idea of making a coloring book app for the Arduboy due to the absurdity of it. The screen is black and white, so you don’t have a lot of choices when it comes to coloring things in. However, there’s a game on the Nintendo 3DS I got years ago called Puzzler, and one of the puzzle types on it is called “Fill in the Pix”. In this, you move across a large black-and-white space with lots of different regions. Some have dots in them, and you tap on those to fill in the area. When you’re done, you’re left with a black-and-white cartoon with some sort of punchline.

In taking this concept to the Arduboy, the biggest technical challenge was figuring out how to represent the bitmap. I wanted a bitmap much larger than the screen to give a sense of scale, so I picked 512x256, which is similar to the size of the original Macintosh screen. I also wanted the drawing of this screen to be relatively quick, so I came up with an encoding scheme that converted the B/W bitmap into a 256 color bitmap, with each region represented as a different color value and a palette mapping in the decoder that picks if each region is black or white or gray. I ended up having the external lines of the image coded as 0, internal lines as 1, unfilled regions as 2-127, and filled regions as 128-255.

To produce my first bitmap, I started with some B/W line art and used a paint.net filter to create an 2-pixel outline on a layer, then deleted the original art and started doing freehand drawing of the lines. Then, I moved from paint.net to Grafx2, another open source tool that is similar to the classic Deluxe Paint and designed for working with 8-bit palette images. I made a “editing palette” where all the colors were different and a few preview palettes which would show me just outlines or the filled image.

dragon_test_512x256

To convert this into something the Arduboy can use, I wrote a Python script to load the PNG, then output it as C source with a simple RLE format. For the testing code, I’m not yet loading from the FX chip, but just saving it to the program flash. Using that scheme, the dragon bitmap compresses to 15738 bytes. Right now, I’m saving the length in bytes of each row at the start of the row, but when converting this to FX, I’ll move all those row offsets to the start of the image so they can be read at once, then we just read the row data for the rows that will be shown on screen.

I also still need to write my scaled drawing code that does the 4:1 downscale of the bitmap to show an overview of the image. That will be used when you’ve completed the puzzle.

I’m planning another non-FX test soon, once I get some sort of cursor control implemented. This test just automatically scrolls around the edge of the picture with three different palette settings. It’s running mostly at 60fps, but occasionally you’ll see the TX LED light up, indicating a frame took a little long. I’ll do perf optimization once I’ve incorporated the FX data, since reading from SPI flash will be a bit less performant than reading from PROGMEM.

3 Likes

Love this! What a neat idea :smiley:
Perhaps you could add some nice dither patterns?
I’m sure you’ve looked at this, but @zep’s CABI is an existing RLE format with builtin support arduboy.drawCompressed.
There was also ArdBitmap with a compression scheme.
Also quad tree might be neat for compression and provide regional access to the map…?

The existing RLE formats are for 1-bit images, not 8-bit ones, AFAIK. They also aren’t optimized for random access to sections of the bitmap via serial flash reads. I might investigate a quadtree like structure, but since I can’t decompress the whole thing into memory, I’d need to see how well I could pull out one section. With row-by-row RLE, I just need to jump to the right row, then decode the whole row, only drawing the needed horizontal window.

1 Like

Maybe I am not understanding why you did it, but you can have massive images in the FX in native formats. It handles the cropping of it automatically.

The 4:! could simply be a second image on the FX.

Here’s an open loop version of FX::readDataBytes:

Summary
static __attribute__((naked, noinline))
void platform_fx_read_data_bytes(uint24_t addr, void* dst, size_t num)
{
    // addr: r22,r23,r24
    // dst:  r20,r21
    // num:  r18,r19
    asm volatile(R"ASM(

            cbi   %[fxport], %[fxbit]
            ldi   r25, %[sfc_read]
            out   %[spdr], r25
            lds   r0, %[page]+0         ;  2
            add   r23, r0               ;  1
            lds   r0, %[page]+1         ;  2
            adc   r24, r0               ;  1
            rcall L%=_delay_11          ; 11
            out   %[spdr], r24
            rcall L%=_delay_17          ; 17
            out   %[spdr], r23
            rcall L%=_delay_17          ; 17
            out   %[spdr], r22
            rcall L%=_delay_17          ; 17
            out   %[spdr], r1

            ; skip straight to final read if num == 1
            movw  r26, r20              ;  1
            subi  r18, 1                ;  1
            sbci  r19, 0                ;  1
            breq  2f                    ;  1 (2)
            adiw  r30, 0                ;  2

            ; intermediate reads
        1:  rcall L%=_delay_10          ; 10
            in    r0, %[spdr]           ;  1
            out   %[spdr], r1
            st    X+, r0                ;  2
            subi  r18, 1                ;  1
            sbci  r19, 0                ;  1
            brne  1b                    ;  2 (1)

            ; final read
        2:  rcall L%=_delay_11          ; 11
            in    r0, %[spdr]
            st    X, r0
            sbi   %[fxport], %[fxbit]
            in    r0, %[spsr]
            ret

        L%=_delay_17:
            lpm
        L%=_delay_14:
            lpm
        L%=_delay_11:
            nop
        L%=_delay_10:
            lpm
        L%=_delay_7:
            ret       ; rcall is 3, ret is 4 cycles

        )ASM"
        :
        : [page]     ""  (&FX::programDataPage)
        , [sfc_read] "I" (SFC_READ)
        , [spdr]     "I" (_SFR_IO_ADDR(SPDR))
        , [spsr]     "I" (_SFR_IO_ADDR(SPSR))
        , [fxport]   "I" (_SFR_IO_ADDR(FX_PORT))
        , [fxbit]    "I" (FX_BIT)
        );
}
2 Likes

I need this image to be in 8-bit format, because during the game play, I use the “color” value of each pixel to decide if it will be drawn as black, white, or gray. When a user fills in a region, the game state that changes is changing the palette to indicate that the region the user was hovering over has a new color. The images remain read-only in the flash memory, it’s just the interpretation of that color data that changes depending on the moves that have been made.

I’ve gone ahead and pushed my code up to GitHub at GitHub - unwiredben/arduboy-find-the-story, I’ll continue to update that as development continues.

1 Like

Ah, OK I see what you are doing - my mistake :slight_smile:

1 Like

Spent some time on this on Sunday, but mostly was fighting with the build system. I’d been using Platform.Io for Arduboy development, but the FX libraries aren’t in its registry. I was able to add the main ArduboyFX library using the “GitHub - MrBlinky/ArduboyFX: Library for accessing the external flash memory of the Arduboy FX and home made Arduboys with a flash chip.” GitHub URL, but I spent much too long trying to figure out how to add the fx build and deploy scripts into the build. It wouldn’t recognize GitHub - MrBlinky/Arduboy-FX-Arduino-plugin: An Arduino tool plugin for easy building and uploading of Arduboy FX data from the Arduino IDE tool menu.. as a URL for a “package”, so I played with the idea of cloning that and adding the needed metadata, but couldn’t find very good documentation on adding tooling into the Package.IO system.

I’m probably just going to switch back to the Arduino 1.8 IDE to try to get this finished.

I also spent some time understanding the fx_data.txt syntax, and probably should contribute some documentation and patches. It would be useful for the fx.data.h header to also include section lengths as constants, not just the start offsets. It generates width/height constants for images, but if you pull in a binary file, the only way to get its length is to subtract from a label that follows it. My current PNG-to-header generator can also just be modified to produce the fx_data.txt file, since the parser handles C data structure syntax in a reasonable manner.

3 Likes

Please do! The more resources the better :cowboy_hat_face:
I’m looking forward to your released game :paintbrush: :art: :framed_picture:

2 Likes

The lack of documentation for the fxdata syntax has thrown me a bit as well. Something in WSN or EBNF, or even MF or a Syntax Diagram would be particularly useful.

Please do. I’ve made a start by providing some Doxygen comments when I added some templated functions to the library, so some more of that would be handy.

I was intending to add some myself but I’ve been a tad preoccupied.

Definitely. Start, length, and end (start + length) would be ideal.

1 Like

I didn’t have a chance to work on it again, so I missed the jam deadline. I still want to finish it, but I don’t know when that will be.

2 Likes