Need help with euclidean distances and CLUTs. [SOLVED!]

Started by Scavenger, Wed 27/10/2010 01:12:20

Previous topic - Next topic

Scavenger

So I've been trying to implement lots of fancy graphical functions into my game, while I wait for my team to get back to me on the game's plot.

I've implemented colourisation pretty easily, and it showed that in 8-bit graphics mode, Get/PutPixel is actually pretty fast. So I tried to make a translucency for objects and special effects, but it seems to have gone belly up. Any object that's blitted in this manner simply turns into a blue cup, and the output I get from ripping the end result is just palette index 0 all the time.

I think it might have been my shoddy attempt at converting this code to AGSScript.

Notably:

Code: ags
     for (i=0; i<64; i++) {
          int i2 = i * i;
          colorDifLut[0  +i] = colorDifLut[0  +128-i] = i2 * (59 * 59);
          colorDifLut[128+i] = colorDifLut[128+128-i] = i2 * (30 * 30);
          colorDifLut[256+i] = colorDifLut[256+128-i] = i2 * (11 * 11);
          }
     }

became:
Code: ags
while (i<64) 
     {
          i2 = i * i;
          colorDifLut[0  +i]     =  i2 * (59 * 59);
          colorDifLut[127+i]     =  i2 * (30 * 30);
          colorDifLut[255+i]     =  i2 * (11 * 11);
          colorDifLut[0  +128-i] =  i2 * (59 * 59);
          colorDifLut[127+128-i] =  i2 * (30 * 30);
          colorDifLut[255+128-i] =  i2 * (11 * 11);
          i++;
     }


and
Code: ags
 for (i=0; i<256; i++) {
     colorDif = colorDifLut[(pal[i].g - g) & 0x7F];
     if (colorDif < lowest) {
          colorDif += (colorDifLut+128)[(pal[i].r -are) & 0x7F];
          if (colorDif < lowest) {
               colorDif += (colorDifLut+256)[(pal[i].b - b) & 0x7F];
               if (colorDif < lowest) {
                    bestMatch = i;
                    if (colorDif == 0)
                         return bestMatch;
                    else
                         lowest = colorDif;
                    }
               }
          }
     }


became:

Code: ags
while (i<256) 
 {
     colorDif = colorDifLut[(palette[i].g - g) & 127];
     if (colorDif < lowest) {
          colorDif += colorDifLut[128+((palette[i].r -are) & 127)];
          if (colorDif < lowest) {
               colorDif += colorDifLut[256+((palette[i].b - b) & 127)];
               if (colorDif < lowest) {
                    bestMatch = i;
                    if (colorDif == 0)
                         return bestMatch;
                    else
                         lowest = colorDif;
                    }
               }
          }
          i++;
 }


I was simply guessing the syntax, since I have never seen syntax like that before in my life. If only I could get it to work, I am so close I can taste it.

If anyone wants to have a go at it, I uploaded the game here. I am way, way out of my league here. If it works correctly, the Zipdisk at the right of the room should render semitransparent. As it is now, it renders a blue cup.

Thanks guys. Sorry for always posting on this subject. x3 At least you get to see some other stuff I've been working on.

Wyz

The conversions look flawless; since the blue cup is incidentally sprite number 0 I guess somewhere in your code the target graphic gets set to 0. I had a look at it but that's a lot of code. Maybe you can recall what portions you edited the last, or you might ask yourself why it has the value 0. That kind of questions often help me debugging.
Life is like an adventure without the pixel hunts.

Scavenger

Quote from: Wyz on Wed 27/10/2010 01:42:37
The conversions look flawless; since the blue cup is incidentally sprite number 0 I guess somewhere in your code the target graphic gets set to 0. I had a look at it but that's a lot of code. Maybe you can recall what portions you edited the last, or you might ask yourself why it has the value 0. That kind of questions often help me debugging.

Thanks! With that in mind, I have progress! There is no longer a blitted blue cup, instead there is transparent space!

So, I dumped the contents of my CLUT for several levels of transparency, and it revealed it was a vast wasteland of Index 0 (with a couple of anomalies). Something has gone terribly wrong. It's not the blitting, but the CLUT generation itself. If it helps:
- The CLUT is generated Before Fade In. It works pretty well for the colourisation function, so I assumed the palette is untouched.
- There are eight levels of CLUT generated, with this algorithm:
Code: ags
    i = 1;
    int j;
    while (i < 8)
    {
      j = i+1 * 32;
      GFX_CreateTransTable (j,  i);
      i++;
    }


It's really frustrating, because it means I interpreted the CLUT generation code wrong. Does anyone have a working function that doesn't have arcane syntax like this one?

Gilbert

I never tried to really read your codes in-depth, but one thing I've noticed is that:
Quote from: Scavenger on Wed 27/10/2010 01:12:20
Code: ags

          colorDifLut[128+i] = colorDifLut[128+128-i] = i2 * (30 * 30);

became:
Code: ags

          colorDifLut[127+i]     =  i2 * (30 * 30);
          colorDifLut[127+128-i] =  i2 * (30 * 30);

There're a number of instances where 128 in the original codes being changed to 127 (and 256 being changed to 255 too). Is that necessary?

Scavenger

Quote from: Gilbet V7000a on Wed 27/10/2010 14:04:18
I never tried to really read your codes in-depth, but one thing I've noticed is that:
Quote from: Scavenger on Wed 27/10/2010 01:12:20
Code: ags

          colorDifLut[128+i] = colorDifLut[128+128-i] = i2 * (30 * 30);

became:
Code: ags

          colorDifLut[127+i]     =  i2 * (30 * 30);
          colorDifLut[127+128-i] =  i2 * (30 * 30);

There're a number of instances where 128 in the original codes being changed to 127 (and 256 being changed to 255 too). Is that necessary?

That was a dirty, dirty hack because if they were the original values, I would get out of bounds errors, and since I don't understand exactly what's going on in the code, I just decremented every top value by 1. If I increased the size of the array from 354 to 355 instead, I don't get the anomalies, I simply get Index 0 all the way down the board.

Scavenger

#5
I have given up trying to use Towner's version, so I rewrote my own colour finder based on the euclidean distance between the two target colours.

Findbestcolour simply finds the total distance between the rgb values of the "ideal" blended value and the actual palette entries and seeks out the one with the lowest difference, and returns that palette index. For some reason it is always returning 0.



Code: ags
function noloopcheck GFX_FindBestColor(int R1, int G1, int B1)
{
int output [256];
int best = 0;
int i;
float prepf;
int prep;
	while (i < 255)
		{	
    prep = (R1-palette[i].r) * (R1-palette[i].r) + (B1-palette[i].b) * (B1-palette[i].b) + (G1-palette[i].g) * (G1-palette[i].g);
    prepf = Maths.Sqrt (IntToFloat (prep));
    output [i] = FloatToInt (prepf, eRoundNearest);
			i++;
		}
  i=0;
	while (i < 255)
		{
			if (output[i] < output [best])
			{
				best = i;
			}
			i++;
		}
	return best;
 }


Code: ags
function noloopcheck GFX_CreateTransTable(int alpha,  char clutnum)
{
  int index = 0, index2 = 0;
  int idealr;
  int idealb;
  int idealg;

  while (index<255) 
  {
       while (index2<255) {
            /* Mix the two colors together based on the alpha */
            idealr = FloatToInt (IntToFloat ((palette[index].r + palette[index2].r)) * (IntToFloat (alpha) / 100.0));

            idealb = FloatToInt (IntToFloat ((palette[index].b + palette[index2].b)) * (IntToFloat (alpha) / 100.0));
            
            idealg = FloatToInt (IntToFloat((palette[index].g + palette[index2].g)) * (IntToFloat (alpha) / 100.0));

            /* Store the blended color into the translucency clut */
            rgblbl.Text = String.Format ("%d %d %d",idealr, idealb, idealg);
            Wait (1);
            clut[clutnum].data[index * 256 + index2] = GFX_FindBestColor(idealr, idealg, idealb);
            index2++;
            lblGameTitle.Text = String.Format ("%d,%d : %d",index2, index, clut[clutnum].data[index * 256 + index2]);
            Wait (1);
       }
   index2=0;
   index++;
  }
}


But, again, it is only returning 0, after returning 64 for palette index 0 itself. I am not actually sure what's going wrong, since it all looks right to me.
(And strangely, it accepted and compiled the script when it had the wrong struct definition. GFXClut_t doesn't haveare b and g variables, but it still compiled.)

Kweepa

The code from gamedev is horribly broken in that it defines a lookup table that is too small for the purpose (its size is 384 but the initialization tries to write to [128+256-0] which is past the end of the array). Who knows what else is wrong with it!

Your code to find the closest colour is a little off too... you need the loop condition to be (i < 256). I'd also recommend just doing it in one loop and not bothering with the sqrt, like so:
// may need to have int as the return type here
int noloopcheck GFX_findbestcolor(int R1, int G1, int B1)
{
  int index;
  int best;
  int bestCost = 3*256*256; // the max the diff can get to
  while (index < 256)
  {
    int dr = R1 - palette[index].r;
    int dg = G1 - palette[index].g;
    int db = B1 - palette[index].b;
    int cost = dr*dr + dg*dg + db*db;
    if (cost < bestCost)
    {
      best = index;
      bestCost = cost;
    }
    index++;
  }
  return best;
}

function noloopcheck GFX_createtranstable(int clutnum, int alpha)
{
  int index;
  int oma = 100-alpha;
  while (index < 256)
  {
    int index2 = 0;
    while (index2 < 256)
    {
      // this is the correct way to blend the colors
      // no need to go from float to int and back
      // the +50 is to make sure it rounds to the nearest
      int idealr = (oma*palette[index].r + alpha*palette[index2].r + 50)/100;
      int idealg = (oma*palette[index].g + alpha*palette[index2].g + 50)/100;
      int idealb = (oma*palette[index].b + alpha*palette[index2].b + 50)/100;
      clut[clutnum].data[256*index + index2] = GFX_findbestcolor(idealr, idealg, idealb);
      index2++;
    }
    index++;
  }
}


Let me know if this doesn't work out for you!
Still waiting for Purity of the Surf II

Scavenger

#7
Wow, that CLUT generation code is beautiful! It's even faster than the one I used, and it even works beautifully. It is a masterpiece.

There's no perceptable slowdown once the actual CLUT is generated (which takes about 5 minutes when you include Wait (1)s for realtime update, longer than the plugin, but more accurate and less green-biased.)

This is what I have so far:
http://img201.imageshack.us/img201/2451/transtest.png
Looks like I'll be adjusting the double buffer next :) The characters are being drawn too far up.

EDIT:



All done! Now everything is in readiness.

Kweepa

Hooray, it worked! Looks good...

Presumably you're going to write the data in the CLUTs to a text file and just read that in on start up - although even that would take a second or two.

To avoid any kind of start up delay, the best way might be to draw each CLUT in a 256x256 dynamic sprite, then write them out and read them in on start up. Should be instantaneous. Instead of looking up clut.data[256*x + y] you'd get the DrawingSurface for the CLUT sprite and use ds.GetPixel(x, y).
Still waiting for Purity of the Surf II

Scavenger

Quote from: SteveMcCrea on Sun 31/10/2010 23:14:25
Hooray, it worked! Looks good...

Presumably you're going to write the data in the CLUTs to a text file and just read that in on start up - although even that would take a second or two.

To avoid any kind of start up delay, the best way might be to draw each CLUT in a 256x256 dynamic sprite, then write them out and read them in on start up. Should be instantaneous. Instead of looking up clut.data[256*x + y] you'd get the DrawingSurface for the CLUT sprite and use ds.GetPixel(x, y).

Hey, that's a good idea. I was thinking about how to compress the CLUTs, since in all each room would take up 512k uncompressed (by having 9 levels of transparency). By making them sprites, it avoids this and stops me from having to litter the game folder with files. (I could probably just save them out and reimport them into AGS, with folders it should make everything much cleaner) I'll work out the logistics tomorrow.

Thanks again!

SMF spam blocked by CleanTalk