X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fgrise.c;h=45fb1376e07340d3f06e0c55d02b2efd838dcd71;hb=14e38e29d030add566b19a06468902c447f1e1b1;hp=7e57508561f3045fd9052c677a1ce19aec73d26a;hpb=67ecd561eda2d5ca1a9a67588f01bd9d530751c0;p=dosdemo diff --git a/src/grise.c b/src/grise.c index 7e57508..45fb137 100644 --- a/src/grise.c +++ b/src/grise.c @@ -9,7 +9,16 @@ /* APPROX. 170 FPS Minimum */ +typedef struct { + unsigned int w, h; + unsigned char *scans; +} RLEBitmap; + +static RLEBitmap createRLEBitmap(unsigned int w, unsigned int h); +static destroyRLEBitmap(RLEBitmap b); + #define BG_FILENAME "data/grise.png" +#define GROBJ_01_FILENAME "data/grobj_01.png" #define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */ @@ -38,6 +47,8 @@ static void processNormal(); static void initScrollTables(); static void updateScrollTables(float dt); +static RLEBitmap rleEncode(unsigned char *pixels, unsigned int w, unsigned int h); + static unsigned short *background = 0; static unsigned int backgroundW = 0; static unsigned int backgroundH = 0; @@ -55,8 +66,10 @@ static int scrollTableRounded[REFLECTION_HEIGHT]; static int scrollModTable[REFLECTION_HEIGHT]; static float nearScrollAmount = 0.0f; +static RLEBitmap grobj; + static struct screen scr = { - "Galaxy rise", + "galaxyrise", init, destroy, start, @@ -72,9 +85,13 @@ struct screen *grise_screen(void) static int init(void) { + unsigned char *tmpBitmap; + int tmpBitmapW, tmpBitmapH; + /* Allocate back buffer */ backBuffer = (unsigned short*) malloc(BB_SIZE * BB_SIZE * sizeof(unsigned short)); + /* grise.png contains the background (horizon), baked reflection and normalmap for displacement */ if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) { fprintf(stderr, "failed to load image " BG_FILENAME "\n"); return -1; @@ -83,6 +100,16 @@ static int init(void) /* Convert to 16bpp */ convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */ + /* Load reflected objects */ + if (!(tmpBitmap = img_load_pixels(GROBJ_01_FILENAME, &tmpBitmapW, &tmpBitmapH, IMG_FMT_GREY8))) { + fprintf(stderr, "failed to load image " GROBJ_01_FILENAME "\n"); + return -1; + } + + grobj = rleEncode(tmpBitmap, tmpBitmapW, tmpBitmapH); + + img_free_pixels(tmpBitmap); + initScrollTables(); processNormal(); @@ -100,6 +127,8 @@ static void destroy(void) backBuffer = 0; img_free_pixels(background); + + destroyRLEBitmap(grobj); } static void start(long trans_time) @@ -131,17 +160,28 @@ static void draw(void) dst += BB_SIZE; } - /* Create scroll opffsets for all scanlines of the normalmap */ + /* Create scroll offsets for all scanlines of the normalmap */ updateScrollTables(lastFrameDuration); - /* Then, render the reflection under the horizon */ - /* dst is already in place */ - src = background + HORIZON_HEIGHT * backgroundW; + /* Render the baked reflection one scanline below its place, so that + * the displacement that follows will be done in a cache-friendly way + */ + src -= PIXEL_PADDING; /* We want to also fill the PADDING pixels here */ + dst = backBuffer + (HORIZON_HEIGHT + 1) * BB_SIZE; + for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) { + memcpy(dst, src, (fb_width + PIXEL_PADDING) * 2); + src += backgroundW; + dst += BB_SIZE; + } + + /* Perform displacement */ + dst = backBuffer + HORIZON_HEIGHT * BB_SIZE + PIXEL_PADDING; + src = dst + BB_SIZE; /* The pixels to be displaced are 1 scanline below */ dispScanline = displacementMap; for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) { for (i = 0; i < fb_width; i++) { d = dispScanline[(i + scrollTableRounded[scanline]) % scrollModTable[scanline]]; - *dst++ = src[i + scroll + d]; + *dst++ = src[i + d]; } src += backgroundW; dst += BB_SIZE - fb_width; @@ -246,3 +286,81 @@ static void updateScrollTables(float dt) { scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i]; } } + +/* ------------------------------------------------------------------------------------------------- + * RLE STUFF + * ------------------------------------------------------------------------------------------------- + */ +/* Limit streak count per scanline so we can directly jump to specific scanline */ +#define RLE_STREAKS_PER_SCANLINE 4 +/* Every streak is encoded by 2 bytes: offset and count of black pixels in the streak */ +#define RLE_BYTES_PER_SCANLINE RLE_STREAKS_PER_SCANLINE * 2 + +static RLEBitmap createRLEBitmap(unsigned int w, unsigned int h) { + RLEBitmap ret; + ret.w = w; + ret.h = h; + + /* Add some padding at the end of the buffer, with the worst case for a scanline (w/2 streaks) */ + ret.scans = (unsigned char*) calloc(h * RLE_BYTES_PER_SCANLINE + w, 1); + + return ret; +} + +static destroyRLEBitmap(RLEBitmap b) { + free(b.scans); +} + +static RLEBitmap rleEncode(unsigned char *pixels, unsigned int w, unsigned int h) { + int scanline; + int i; + int penActive = 0; + int counter = 0; + int accum = 0; + RLEBitmap ret; + unsigned char *output; + + /* https://www.youtube.com/watch?v=RKMR02o1I88&feature=youtu.be&t=55 */ + ret = createRLEBitmap(w, h); + + for (scanline = 0; scanline < h; scanline++) { + output = ret.scans + scanline * RLE_BYTES_PER_SCANLINE; + accum = 0; + for (i = 0; i < w; i++) { + if (*pixels++) { + if (penActive) { + if (counter >= PIXEL_PADDING) { + *output++ = (unsigned char) counter; + counter = 0; + *output++ = (unsigned char)accum; + } + counter++; + accum++; + } else { + *output++ = (unsigned char)accum; + counter = 1; + accum++; + penActive = 1; + } + } else { + if (penActive) { + *output++ = (unsigned char)counter; + counter = 1; + accum++; + penActive = 0; + } else { + counter++; + accum++; + } + } + } + + if (penActive) { + *output++ = (unsigned char)counter; + } + penActive = 0; + counter = 0; + } + + return ret; +}