Generating Multiple CLUTs

Running short on memory, but want to include more graphics? You might consider generating multiple CLUTs for each sprite/texture. This costs only 32 or 512 bytes from the frame buffer (depending on whether you're using 4- or 8-bit mode). I'm doing this for the planets in Decaying Orbit™. Each one has 6 different CLUTs which can be selected. It increases variety without a need for including more TIMs.

Of course you probably don't want to just generate random CLUTs out of the blue. Since adjoining pixels expect to carry some sort of relationship between one another, random colors would make it look like a mess. You can preserve this relationship, however, by using the original CLUT as a starting point. For instance, I generated the extra planet CLUTs by rearranging the Red, Green, and Blue values for each entry. There are six possible ways to rearrange them: RGB (original), RBG, BRG, BGR, GBR, GRB.

Before you start, though you need to leave room in the frame buffer to hold these extra CLUTs. I arranged mine such that I left 5 open rows below the original CLUT. That way I can just fill them in with the alternates and change the cy pointer in the sprite structure.

The code I use to generate the alternate CLUTs is given below. I hacked it somewhat to take out the code which was specific to my program. As such it may not compile directly. I'm only using 4-bit TIMs, but it's easy to change it to work with 8-bit TIMs too:

//**********************************************************************
static void init_cluts(GsSPRITE *sprt)
{
  int entry_num, clut_variety;
  RECT clut_rect;
  u_long tmp_buffer[8], tmp_new_buffer[8];
  u_short *buffer, *new_buffer;
  u_short red, green, blue;

  buffer     = (u_short *) tmp_buffer;
  new_buffer = (u_short *) tmp_new_buffer;

  clut_rect.x = sprt->cx;
  clut_rect.y = sprt->cy;
  clut_rect.w = 16;
  clut_rect.h = 1;

  // Read in the original CLUT as 8 u_longs
  StoreImage(&clut_rect, tmp_buffer);

  // There are 6 combinations of RGB ordering.  Skip the first one (0)
  // since that is the original.
  for (clut_variety = 1; clut_variety <= 5; clut_variety++) {

    // 16 CLUT entries
    for (entry_num = 0; entry_num <= 15; entry_num++) {

      // Get the R, G, and B values
      red   =  buffer[entry_num] & 0x001F;
      green = (buffer[entry_num] & 0x03E0) >> 5;
      blue  = (buffer[entry_num] & 0x7C00) >> 10;

      // Assemble the new CLUT entry based on the variety we're using
      //
      // Variety  R   G   B
      // ------- --- --- ---
      //    0     0   1   2
      //    1     0   2   1
      //    2     1   0   2
      //    3     2   0   1
      //    4     1   2   0
      //    5     2   1   0
      //
      switch (clut_variety) {
        case 0:  new_buffer[entry_num] = red   | (green << 5) | (blue  << 10);
                 break;
        case 1:  new_buffer[entry_num] = red   | (blue  << 5) | (green << 10);
                 break;
        case 2:  new_buffer[entry_num] = green | (red   << 5) | (blue  << 10);
                 break;
        case 3:  new_buffer[entry_num] = green | (blue  << 5) | (red   << 10);
                 break;
        case 4:  new_buffer[entry_num] = blue  | (red   << 5) | (green << 10);
                 break;
        default: new_buffer[entry_num] = blue  | (green << 5) | (red   << 10);
                 break;
      }
    }

    // Increment where we're storing the new CLUT
    clut_rect.y++;
    LoadImage(&clut_rect, tmp_new_buffer);
  }
}

Of course this isn't the only way. You could probably come up with different methods for creating new CLUTs other than rearranging the RGB values. But you get the idea.



Top

This web page and all other pages on this site are © 1997 Scott Cartier